I have a FixedDocument that I allow the user to preview in a WPF GUI and then print to paper without showing any Windows printing dialogue, like so:
private void Print()
{
PrintQueueCollection printQueues;
using (var printServer = new PrintServer())
{
var flags = new[] { EnumeratedPrintQueueTypes.Local };
printQueues = printServer.GetPrintQueues(flags);
}
//SelectedPrinter.FullName can be something like "Microsoft Print to PDF"
var selectedQueue = printQueues.SingleOrDefault(pq => pq.FullName == SelectedPrinter.FullName);
if (selectedQueue != null)
{
var myTicket = new PrintTicket
{
CopyCount = 1,
PageOrientation = PageOrientation.Portrait,
OutputColor = OutputColor.Color,
PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4)
};
var mergeTicketResult = selectedQueue.MergeAndValidatePrintTicket(selectedQueue.DefaultPrintTicket, myTicket);
var printTicket = mergeTicketResult.ValidatedPrintTicket;
// TODO: Make sure merge was OK
// Calling GetPrintCapabilities with our ticket allows us to use
// the OrientedPageMediaHeight/OrientedPageMediaWidth properties
// and the PageImageableArea property to calculate the minimum
// document margins supported by the printer. Very important!
var printCapabilities = queue.GetPrintCapabilities(myTicket);
var fixedDocument = GenerateFixedDocument(printCapabilities);
var dlg = new PrintDialog
{
PrintTicket = printTicket,
PrintQueue = selectedQueue
};
dlg.PrintDocument(fixedDocument.DocumentPaginator, "test document");
}
}
The problem is that I want to also support virtual/file printers, namely PDF printing, by giving the file destination path and not showing any Windows dialogues, but that doesn't seem to work with the PrintDialog.
I would really like to avoid 3rd party libraries as much as possible, so at least for now, using something like PdfSharp to convert an XPS to PDF is not something I want to do. Correction: It seems like XPS conversion support was removed from the latest version of PdfSharp.
After doing some research, it seems the only way to print straight to a file is to use a PrintDocument where it's possible to set PrintFileName and PrintToFile in the PrinterSettings object, but there is no way to give the actual document contents, rather we need to subscribe to the PrintPage event and do some System.Drawing.Graphics manipulation where the document is created.
Here's the code I tried:
var printDoc = new PrintDocument
{
PrinterSettings =
{
PrinterName = SelectedPrinter.FullName,
PrintFileName = destinationFilePath,
PrintToFile = true
},
PrintController = new StandardPrintController()
};
printDoc.PrintPage += OnPrintPage; // Without this line, we get a blank PDF
printDoc.Print();
Then the handler for PrintPage where we need to build the document:
private void OnPrintPage(object sender, PrintPageEventArgs e)
{
// What to do here?
}
Other things that I thought could work are using the System.Windows.Forms.PrintDialog class instead, but that also expects a PrintDocument. I was able to create an XPS file easily like so:
var pkg = Package.Open(destinationFilePath, FileMode.Create);
var doc = new XpsDocument(pkg);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(PreviewDocument.DocumentPaginator);
pkg.Flush();
pkg.Close();
But it's not a PDF, and there seems to be no way to convert it to PDF without a 3rd party library.
Is it possible to maybe do a hack that automatically fills the filename then clicks save on the PrintDialog?
Thank you!
EDIT: It's possible to print directly to PDF from Word documents using Microsoft.Office.Interop.Word, but there seems to be no easy way of converting from XPS/FixedDocument to Word.
EDIT: It seems so far the best way is to grab the old XPS to PDF conversion code that was present in PdfSharp 1.31. I grabbed the source code and built it, imported the DLL's, and it works. Credit goes to Nathan Jones, check out his blog post about this here.
Solved! After googling around I was inspired by the P/Invoke method of directly calling Windows printers.
So the solution is to use the Print Spooler API functions to directly call the Microsoft Print to PDF printer available in Windows (make sure the feature is installed though!) and giving the WritePrinter function the bytes of an XPS file.
I believe this works because the Microsoft PDF printer driver understands the XPS page description language. This can be checked by inspecting the IsXpsDevice property of the print queue.
The "Microsoft Print to PDF" feature must be installed in Windows for this to work!
Here's the code:
using System;
using System.Linq;
using System.Printing;
using System.Runtime.InteropServices;
public static class PdfFilePrinter
{
private const string PdfPrinterDriveName = "Microsoft Print To PDF";
[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;
}
[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, 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.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int StartDocPrinter(IntPtr hPrinter, int 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, int dwCount, out int dwWritten);
public static void PrintXpsToPdf(byte[] bytes, string outputFilePath, string documentTitle)
{
// Get Microsoft Print to PDF print queue
var pdfPrintQueue = GetMicrosoftPdfPrintQueue();
// Copy byte array to unmanaged pointer
var ptrUnmanagedBytes = Marshal.AllocCoTaskMem(bytes.Length);
Marshal.Copy(bytes, 0, ptrUnmanagedBytes, bytes.Length);
// Prepare document info
var di = new DOCINFOA
{
pDocName = documentTitle,
pOutputFile = outputFilePath,
pDataType = "RAW"
};
// Print to PDF
var errorCode = SendBytesToPrinter(pdfPrintQueue.Name, ptrUnmanagedBytes, bytes.Length, di, out var jobId);
// Free unmanaged memory
Marshal.FreeCoTaskMem(ptrUnmanagedBytes);
// Check if job in error state (for example not enough disk space)
var jobFailed = false;
try
{
var pdfPrintJob = pdfPrintQueue.GetJob(jobId);
if (pdfPrintJob.IsInError)
{
jobFailed = true;
pdfPrintJob.Cancel();
}
}
catch
{
// If job succeeds, GetJob will throw an exception. Ignore it.
}
finally
{
pdfPrintQueue.Dispose();
}
if (errorCode > 0 || jobFailed)
{
try
{
if (File.Exists(outputFilePath))
{
File.Delete(outputFilePath);
}
}
catch
{
// ignored
}
}
if (errorCode > 0)
{
throw new Exception($"Printing to PDF failed. Error code: {errorCode}.");
}
if (jobFailed)
{
throw new Exception("PDF Print job failed.");
}
}
private static int SendBytesToPrinter(string szPrinterName, IntPtr pBytes, int dwCount, DOCINFOA documentInfo, out int jobId)
{
jobId = 0;
var dwWritten = 0;
var success = false;
if (OpenPrinter(szPrinterName.Normalize(), out var hPrinter, IntPtr.Zero))
{
jobId = StartDocPrinter(hPrinter, 1, documentInfo);
if (jobId > 0)
{
if (StartPagePrinter(hPrinter))
{
success = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
// TODO: The other methods such as OpenPrinter also have return values. Check those?
if (success == false)
{
return Marshal.GetLastWin32Error();
}
return 0;
}
private static PrintQueue GetMicrosoftPdfPrintQueue()
{
PrintQueue pdfPrintQueue = null;
try
{
using (var printServer = new PrintServer())
{
var flags = new[] { EnumeratedPrintQueueTypes.Local };
// FirstOrDefault because it's possible for there to be multiple PDF printers with the same driver name (though unusual)
// To get a specific printer, search by FullName property instead (note that in Windows, queue name can be changed)
pdfPrintQueue = printServer.GetPrintQueues(flags).FirstOrDefault(lq => lq.QueueDriver.Name == PdfPrinterDriveName);
}
if (pdfPrintQueue == null)
{
throw new Exception($"Could not find printer with driver name: {PdfPrinterDriveName}");
}
if (!pdfPrintQueue.IsXpsDevice)
{
throw new Exception($"PrintQueue '{pdfPrintQueue.Name}' does not understand XPS page description language.");
}
return pdfPrintQueue;
}
catch
{
pdfPrintQueue?.Dispose();
throw;
}
}
}
Usage:
public static void FixedDocument2Pdf(FixedDocument fd)
{
// Convert FixedDocument to XPS file in memory
var ms = new MemoryStream();
var package = Package.Open(ms, FileMode.Create);
var doc = new XpsDocument(package);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(fd.DocumentPaginator);
doc.Close();
package.Close();
// Get XPS file bytes
var bytes = ms.ToArray();
ms.Dispose();
// Print to PDF
var outputFilePath = #"C:\tmp\test.pdf";
PdfFilePrinter.PrintXpsToPdf(bytes, outputFilePath, "Document Title");
}
In the code above, instead of directly giving the printer name, I get the name by finding the print queue using the driver name because I believe it's constant while the printer name can actually be changed in Windows, also I don't know if it's affected by localization so this way is safer.
Note: It's a good idea to check available disk space size before starting the printing operation, because I couldn't find a way to reliably find out if the error was insufficient disk space. One idea is to multiply the XPS byte array length by a magic number like 3 and then check if we have that much space on disk. Also, giving an empty byte array or one with bogus data does not fail anywhere, but produces a corrupt PDF file.
Note from comments:
Simply reading an XPS file using FileStream will not work. We have to create an XpsDocument from a Package in memory, then read the bytes from the MemomryStream like this:
public static void PrintFile(string xpsSourcePath, string pdfOutputPath)
{
// Write XPS file to memory stream
var ms = new MemoryStream();
var package = Package.Open(ms, FileMode.Create);
var doc = new XpsDocument(package);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
writer.Write(xpsSourcePath);
doc.Close();
package.Close();
// Get XPS file bytes
var bytes = ms.ToArray();
ms.Dispose();
// Print to PDF
PdfPrinter.PrintXpsToPdf(bytes, pdfOutputPath, "Document title");
}
Related
I'm trying to programmatically print pdfs using c#. I tried different libraries (PostSharp, PDFium, etc.) as well as the adobe SDK. However, I have not been able to find anything about printing and stapling the printouts.
I tried using the PrintTicket object (Microsoft) to set the Stapling property but it's not working. I have verified that I have the right print drivers installed. I am able to staple printouts when I print manually so I'm sure the printer supports it.
I edited the post to include the full code. The PDF library I'm using here is PdfiumViewer. I'm not checking the return value in this code but if I run this, the return value I get gives me "ConflictStatus.ConflictResolved".
using (var server = new PrintServer("print server name"))
{
var printerName = "printer with stapling capabilities name";
var queues = server.GetPrintQueues();
using (var queue = queues.FirstOrDefault(x => x.FullName == printerName))
{
var printTicket = queue.DefaultPrintTicket;
printTicket.Collation = Collation.Collated;
printTicket.Stapling = Stapling.StapleTopLeft;
queue.UserPrintTicket = printTicket;
queue.CurrentJobSettings.CurrentPrintTicket = printTicket;
var ret = queue.MergeAndValidatePrintTicket(queue.UserPrintTicket, printTicket);
using (var pdfDoc = PdfDocument.Load("path to pdf"))
{
using (var printDoc = pdfDoc.CreatePrintDocument())
{
printDoc.PrinterSettings.PrinterName = printerName;
printDoc.PrinterSettings.ToPage = 2;
printDoc.Print();
}
}
}
}
I finally found something that works! I got the code from the following: https://www.codeproject.com/Articles/488737/Storing-and-recalling-printer-settings-in-Csharp-n
public static class PrinterUtilities
{
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalUnlock(IntPtr handle);
/// <summary>
/// Grabs the data in arraylist and chucks it back into memory "Crank the suckers out"
/// </summary>
/// <param name="printerSettings"></param>
/// <param name="filename"></param>
public static void SetDevModeFromFile(PrinterSettings printerSettings, string filename)
{
IntPtr hDevMode = IntPtr.Zero;// a handle to our current DEVMODE
IntPtr pDevMode = IntPtr.Zero;// a pointer to our current DEVMODE
try
{
// Obtain the current DEVMODE position in memory
hDevMode = printerSettings.GetHdevmode(printerSettings.DefaultPageSettings);
// Obtain a lock on the handle and get an actual pointer so Windows won't move
// it around while we're futzing with it
pDevMode = GlobalLock(hDevMode);
// Overwrite our current DEVMODE in memory with the one we saved.
// They should be the same size since we haven't like upgraded the OS
// or anything.
var fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
var temparray = new byte[fs.Length];
fs.Read(temparray, 0, temparray.Length);
fs.Close();
fs.Dispose();
for (int i = 0; i < temparray.Length; ++i)
{
Marshal.WriteByte(pDevMode, i, temparray[i]);
}
// We're done futzing
GlobalUnlock(hDevMode);
// Tell our printer settings to use the one we just overwrote
printerSettings.SetHdevmode(hDevMode);
printerSettings.DefaultPageSettings.SetHdevmode(hDevMode);
// It's copied to our printer settings, so we can free the OS-level one
GlobalFree(hDevMode);
}
catch (Exception)
{
if (hDevMode != IntPtr.Zero)
{
GlobalUnlock(hDevMode);
// And to boot, we don't need that DEVMODE anymore, either
GlobalFree(hDevMode);
hDevMode = IntPtr.Zero;
}
}
}
}
then calling it in my main class
private static void PrintTest()
{
using (var pdfDoc = PdfDocument.Load("pdf doc path"))
{
using (var printDoc = pdfDoc.CreatePrintDocument())
{
var currentSettings = printDoc.PrinterSettings;
SetDevModeFromFile(currentSettings, "bin file that has printer settings path");
currentSettings.ToPage = 3;
printDoc.Print();
}
}
}
To use the code above, I had to download the original solution from the link. I then ran the original solution in order to update the staple/finishing settings from the printer dialog and then save the settings to a "bin" file. The issue seems to be that not all the settings are exposed by .net (possibly because these are more printer hardware-specific settings). To open the pdf file, I used PDFiumViewer. I hope this helps someone else.
I want to use c # write a UI Automation program, but I can't find some elements with "Inspect.exe", Can't find pictures of text labels(eg. image1), why?
image1: https://i.stack.imgur.com/NqpKA.png
image2: https://i.stack.imgur.com/2PIuj.png
Code sample:
var desktop = AutomationElement.RootElement;
var condition = new PropertyCondition(AutomationElement.NameProperty, "Customer Register");
var window = desktop.FindFirst(System.Windows.Automation.TreeScope.Children, condition);
The code you posted for getting the elements is correct from what I can see in your screen shot from inspect. I have a feeling that the UI Automation is not getting the name back because of the text encoding. Unless you have access to the source and can mess with how it gets and sets text for those labels it won't be possible to retrieve the text through UI Automation.
You could use the code you have and use the native window handle for the window with the win32 api to get the text.
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, [Out] StringBuilder lParam);
public static string GetWindowTextRaw(IntPtr hwnd)
{
// Allocate correct string length first
int length = (int)SendMessage(hwnd, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero);
StringBuilder sb = new StringBuilder(length + 1);
SendMessage(hwnd, WM_GETTEXT, (IntPtr)sb.Capacity, sb);
return sb.ToString();
}
public static void YourMethod()
{
var desktop = AutomationElement.RootElement;
var process = Process.Start("Path/To/Your/Process.exe");
var condition = new PropertyCondition(AutomationElement.ProcessId, process.Id);
var window = desktop.FindFirst(System.Windows.Automation.TreeScope.Children, condition);
var windowTitle = GetWindowTextRaw(window.NativeWindowHandle)
}
Sources:
GetWindowText
SendMessage
I am trying to upgrade to the MS .NET Framework 4 and I am encountering an error with a dllimport function (see below). When the code reaches the dllimport, the program just exits with error code 0x80000003. I have tried changing it so the input and output are StringBuilder and I also tried setting the charset by both changing the charset (to either unicode or ansi) and setting a EntryPoint (either PathGetArgsA or PathGetArgsW). This code works fine in v3.5 but not in v4.
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern string PathGetArgs([In] string path);
[DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern void PathRemoveArgs([In, Out] StringBuilder path);
public static bool ExtractArguments(string cmdLine, out string filePath, out string fileArgs)
{
StringBuilder strCmdLine = new StringBuilder(cmdLine.ToLower().Trim());
filePath = fileArgs = "";
if (strCmdLine.Length <= 0)
throw new ArgumentNullException("cmdLine");
fileArgs = string.Copy(PathGetArgs(strCmdLine.ToString())); // Error occurs here
PathRemoveArgs(strCmdLine);
filePath = string.Copy(strCmdLine.ToString());
if (!string.IsNullOrEmpty(filePath))
if (Utils.FileExists(filePath))
return true;
return false;
}
Thanks!
I'm not sure what the native dll retuns in the PathGetArgs however, the Marshal Class could help.
[DllImport("Shlwapi.dll")]
public static extern IntPtr PathGetArgs([In] string path);
fileArgs = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PathGetArgs(strCmdLine.ToString()));
1.)
How do Load, Edit and Save binary Hive files for registry from C#?
I found this Win32 api.
http://msdn.microsoft.com/en-us/library/ee210770%28VS.85%29.aspx
This guy shared the code to dump the content of binary Hive files to text.
http://www.codeproject.com/KB/recipes/RegistryDumper.aspx
2.)
In addition to manipulating the Hive files, I also search for a method to load the Hive file into registry at runtime using C#
(similar to the Load Hive and Unload Hive commands on the File many in regedit)
/Thanks
Have you looked at the Registry and RegistryKey classes in Microsoft.Win32?
http://msdn.microsoft.com/en-us/library/microsoft.win32.aspx
It sounds like you may need to create your own representation to read the hive file and either queue up or immediately make the corresponding registry changes. Likewise you would need to write your own converter back to disk.
The article below explains how to analyze the registry file without using WinAPI (advapi32.dll). In this particular case the guy is using Mono:
http://volatile-minds.blogspot.com/2011/01/analyzing-windows-nt-registry-without.html
using (FileStream fs = File.OpenRead (path)) {
var data = new byte[checked((int)fs.Length)];
int i = 0;
int read;
using (var ms = new MemoryStream (checked((int)fs.Length))) {
while ((read = fs.Read (data, 0, data.Length)) > 0) {
ms.Write (data, 0, read);
i += read;
}
byte[] hive = ms.ToArray ();
char[] cList = new char[fs.Length];
i = 0;
foreach (byte b in hive)
cList[i++] = (char)b;
string d = new string (cList);
int all = 0;
foreach (Match mx in lf.Matches (d)) { //you can change out the regex you want here.
byte[] bb = new byte[mx.Value.Length];
char[] cb = new char[mx.Value.Length];
for (int k = 0; k < mx.Value.Length; k++) {
bb[k] = (byte)mx.Value[k];
cb[k] = (char)bb[k];
}
all++;
//Console.WriteLine (new string (cb));
}
Console.WriteLine (all.ToString ());
all = 0;
}
}
This is 9 years old, but I figured this could help someone else. I wrote this class that allows you to do something like this:
Hive.AcquirePrivileges() // Acquires the privileges necessary for loading the hive
Hive myregistryhive = Hive.LoadFromFile("hivepathhere") // Loads the hive
// use myregistryhive.RootKey (a RegistryKey), read and/or write to it and its sub keys
myregistryhive.SaveAndUnload() // Unloads the hive
Hive.ReturnPrivileges() // De-elevate back to normal privileges.
The code for the class:
class Hive
{
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegLoadKey(IntPtr hKey, string lpSubKey, string lpFile);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegSaveKey(IntPtr hKey, string lpFile, uint securityAttrPtr = 0);
[DllImport("advapi32.dll", SetLastError = true)]
static extern int RegUnLoadKey(IntPtr hKey, string lpSubKey);
[DllImport("ntdll.dll", SetLastError = true)]
static extern IntPtr RtlAdjustPrivilege(int Privilege, bool bEnablePrivilege, bool IsThreadPrivilege, out bool PreviousValue);
[DllImport("advapi32.dll")]
static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref UInt64 lpLuid);
[DllImport("advapi32.dll")]
static extern bool LookupPrivilegeValue(IntPtr lpSystemName, string lpName, ref UInt64 lpLuid);
private RegistryKey parentKey;
private string name;
private string originalPath;
public RegistryKey RootKey;
private Hive() { }
public static Hive LoadFromFile(string Path)
{
Hive result = new Hive();
result.parentKey = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
result.name = Guid.NewGuid().ToString();
result.originalPath = Path;
IntPtr parentHandle = result.parentKey.Handle.DangerousGetHandle();
RegLoadKey(parentHandle, result.name, Path);
//Console.WriteLine(Marshal.GetLastWin32Error());
result.RootKey = result.parentKey.OpenSubKey(result.name, true);
return result;
}
public static void AcquirePrivileges()
{
ulong luid = 0;
bool throwaway;
LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
RtlAdjustPrivilege((int)luid, true, false, out throwaway);
LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
RtlAdjustPrivilege((int)luid, true, false, out throwaway);
}
public static void ReturnPrivileges()
{
ulong luid = 0;
bool throwaway;
LookupPrivilegeValue(IntPtr.Zero, "SeRestorePrivilege", ref luid);
RtlAdjustPrivilege((int)luid, false, false, out throwaway);
LookupPrivilegeValue(IntPtr.Zero, "SeBackupPrivilege", ref luid);
RtlAdjustPrivilege((int)luid, false, false, out throwaway);
}
public void SaveAndUnload()
{
RootKey.Close();
RegUnLoadKey(parentKey.Handle.DangerousGetHandle(), name);
parentKey.Close();
}
}
Edit: Note that this requires administrator privileges.
please see: https://github.com/brandonprry/volatile_reader
It reads offline hives in C# with a GTK interface. No write support yet though.
I'm using Visual Studio 2010 to create a Word Template. I created a Ribbon with to buttons: print in color, print in B&W. I use the Document.printout() function to print the document.
How can I set the printer to Grayscale printing from code?
I don't want to use the printDialog.
I tried to use this:
PrinterSettings settings = new PrinterSettings();
settings.DefaultPageSettings.Color = false;
But this doesn't work in combination with Word
I Found a solution with the DEVMODE and some pInvokes;
Devmode: (http://msdn.microsoft.com/en-us/library/aa927408.aspx)
This structure contains information about a printer environment and device initialization.
It contains a field: dmColor (short) setting this to 1 means grayscale/monoschrome, settings this to 2 means color. Changing this settings effects the printer directly and overrides user settings.
[DllImport("winspool.drv", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern bool SetPrinter(IntPtr hPrinter, int Level, IntPtr pPrinter, int command);
I used this example to create my code
public bool setPrinterToGrayScale(string printerName)
{
short monochroom = 1;
dm = this.GetPrinterSettings(printerName);
dm.dmColor = monochroom;
Marshal.StructureToPtr(dm, yDevModeData, true);
pinfo.pDevMode = yDevModeData;
pinfo.pSecurityDescriptor = IntPtr.Zero;
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 System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
if (hPrinter != IntPtr.Zero)
ClosePrinter(hPrinter);
return Convert.ToBoolean(nRet);
}
PrinterName can be retrieved via:
System.Drawing.Printing.PrinterSettings.InstalledPrinters