Hopefully somebody here can help me out as I am banging my head against a wall trying to get this to work. Basically I need to print using the winspool.Drv win32 API to send raw text/PCL commands to the printer. This works fine, however, none of the printer options you choose in the printer dialog / preferences do anything.
Eventually I want to get the DEVMODE from the dialogs PrinterSettings, and use that for the raw printing, but I cannot even get it to work setting it manually.
I have tried changing the DEVMODE right before WritePrinter, or before in a separate Open/Close printer, I have tried using only DocumentProperties calls, and using GetPrinter/SetPrinter with a PRINTER_INFO_8 structure, nothing sticks. The DEVMODE structure seems to fill out correctly, I change it, and it seems to change correctly, but the printer always prints the same exact way no matter what.
Here is the code ive been using:
Method1:
public static bool SetPrinterSettings(PrinterSettings settings, IntPtr hPrinter)
{
IntPtr hDevMode; // a handle to our current DEVMODE
IntPtr pDevMode; // a pointer to our current DEVMODE
String sPrinterName; // normalized printer name
DEVMODE dm;
// Setup
sPrinterName = settings.PrinterName.Normalize();
// Obtain the current DEVMODE from the PrinterSettings
hDevMode = settings.GetHdevmode(settings.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);
// test
dm = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE));
// Our DEVMODE is 188 bytes, but dmSize returns 220, does this matter?
// This code doesnt seem to work
//dm.dmSize = (short)Marshal.SizeOf(dm); // Set size to our implementation of DevMode
//int isize = GlobalSize(pDevMode).ToInt32() - (int)dm.dmSize; // Set the print drivers extra size
//dm.dmDriverExtra = Convert.ToInt16(isize);
// Change things
dm.dmFields = DM_FIELD_TYPE.DM_ORIENTATION;
dm.dmOrientation = 2;
// Load the structure back into the buffer
Marshal.StructureToPtr(dm, pDevMode, true);
//Tell the printer about the new property
int ret = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pDevMode, pDevMode, (DM_IN_BUFFER | DM_OUT_BUFFER));
if (ret <= 0)
{
return false;
}
// We're done futzing
GlobalUnlock(hDevMode);
// Tell our printer settings to use the one we just overwrote
//settings.SetHdevmode(hDevMode);
// It's copied to our printer settings, so we can free the OS-level one
GlobalFree(hDevMode);
//test
dm = (DEVMODE)Marshal.PtrToStructure(pDevMode, typeof(DEVMODE));
// ^^ this contains correct orientation
return true;
}
Method2:
public static bool SetLandscapeMode(PrinterSettings settings, IntPtr hPrinter)
{
// Setup
String sPrinterName = settings.PrinterName.Normalize();
// get current printer settings
int memNeeded = DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, IntPtr.Zero, IntPtr.Zero, 0);
IntPtr pFullDevMode = Marshal.AllocHGlobal(memNeeded);
DocumentProperties(IntPtr.Zero, hPrinter, sPrinterName, pFullDevMode, IntPtr.Zero, DM_OUT_BUFFER);
DEVMODE dm = (DEVMODE)Marshal.PtrToStructure(pFullDevMode, typeof(DEVMODE));
// change the settings
dm.dmFields = DM_FIELD_TYPE.DM_ORIENTATION;
dm.dmOrientation = 2;
Marshal.StructureToPtr(dm, pFullDevMode, true);
PRINTER_INFO_8 PI8 = new PRINTER_INFO_8();
PI8.pDevMode = pFullDevMode;
IntPtr pPI8 = Marshal.AllocHGlobal(Marshal.SizeOf(PI8));
Marshal.StructureToPtr(PI8, pPI8, true);
// save the printer settings
SetPrinter(hPrinter, 9, pPI8, 0);
Marshal.FreeHGlobal(pPI8);
return true;
}
Code that tests the printing:
private void testPrint_Click(object sender, EventArgs e)
{
IntPtr hPrinter;
if (printDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
hPrinter = RawPrint.OpenRAWPrinter(printDialog1.PrinterSettings.PrinterName);
if (hPrinter == IntPtr.Zero)
{
MessageBox.Show("Error opening printer");
return;
}
if (!RawPrint.StartRAWDocument(hPrinter, "Test document"))
{
MessageBox.Show("Error starting raw document");
RawPrint.CloseRAWPrinter(hPrinter);
return;
}
// Start a page.
if (RawPrint.StartRawPage(hPrinter))
{
// Set the printers settings
//if (!RawPrint.SetPrinterSettings(printDialog1.PrinterSettings, hPrinter))
if(!RawPrint.SetLandscapeMode(printDialog1.PrinterSettings, hPrinter))
{
MessageBox.Show("Error setting printer settings");
RawPrint.CloseRAWPrinter(hPrinter);
return;
}
String test = "This is \r\n test print data";
byte[] buf = Encoding.Default.GetBytes(test);
GCHandle gch = GCHandle.Alloc(buf, GCHandleType.Pinned);
RawPrint.writeRAW(hPrinter, gch.AddrOfPinnedObject(), buf.Length);
gch.Free();
RawPrint.EndRawPage(hPrinter);
}
RawPrint.EndRAWDocument(hPrinter);
RawPrint.CloseRAWPrinter(hPrinter);
}
else
{
MessageBox.Show("Error starting raw page");
}
}
The only odd thing is that Marshal.SizeOf(typeof(DEVMODE)) or Marshal.SizeOf(dm) equals 188 whereas the dmSize that is returned into the structure is 220. Ive tried modifying this dmSize to match our structure as shown in the commented out code, but that seems to error out or doesnt work, not sure if that matters.
Besides that the returned info in the DevModes all seems to be correct and working. I can set the options in the print dialog, and see them changed in the PrinterSettings devmode. And at the end of these functions, Marshalling the pDevMode back to a struct has the changed settings.
However, none of this seems to have any effect on the print what so ever. Is there something im missing?
EDIT: The RawPrint calls in the test function are just wrappers around the spooler api calls. The page prints fine, it just always prints in portrait mode and no options work.