Printer Controls - Set Tray
Printer Controls - Set Tray
(OP)
First, I don't know if this is possible...second, it may be doable in some other way I'm unaware of. Here's the situation.
My jury program is designed to send out reminders two weeks before the panelists are scheduled to have orientation. For instance, today we will print the reminder cards for the term beginning in two weeks. These cards are 4 standard postcards on a single 8 1/2 X 11 sheets of paper (they are pre-perforated so after the printing process is complete, they are separated into 4 postcards and ready to mail).
I use a Word Template to print the information side (has a single field where the orientation day is filled in) and then the user puts the paper back in the printer to print the names & addresses on the card.
This all works wonderfully, except the paper is THICK, and it jams if set in Tray 1 of the printer. However, Tray 6 is specifically designed for heavier paper stock.
Does anyone know how I can with a TPrinter object programatically set the Tray of the printer? (I would prefer a programatic option since I have another process that uses thick paper and I could also use this technique here!!) I see the procedure 'GetPrinter', but I'm not sure this will return any information I can use.... Or alternatively, a way to set my Word template to always print from Tray 6? I have tried setting the document to print, but once Word is closed those settings are "forgotten".
Any assistance appreciated!!
thanks!
My jury program is designed to send out reminders two weeks before the panelists are scheduled to have orientation. For instance, today we will print the reminder cards for the term beginning in two weeks. These cards are 4 standard postcards on a single 8 1/2 X 11 sheets of paper (they are pre-perforated so after the printing process is complete, they are separated into 4 postcards and ready to mail).
I use a Word Template to print the information side (has a single field where the orientation day is filled in) and then the user puts the paper back in the printer to print the names & addresses on the card.
This all works wonderfully, except the paper is THICK, and it jams if set in Tray 1 of the printer. However, Tray 6 is specifically designed for heavier paper stock.
Does anyone know how I can with a TPrinter object programatically set the Tray of the printer? (I would prefer a programatic option since I have another process that uses thick paper and I could also use this technique here!!) I see the procedure 'GetPrinter', but I'm not sure this will return any information I can use.... Or alternatively, a way to set my Word template to always print from Tray 6? I have tried setting the document to print, but once Word is closed those settings are "forgotten".
Any assistance appreciated!!
thanks!
Leslie
Anything worth doing is a lot more difficult than it's worth - Unknown Induhvidual
RE: Printer Controls - Set Tray
I've used this successfully to change printer trays:
CODE
//from : http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt
var
ADevice, ADriver, APort: array [0..255] of Char;
DeviceHandle: THandle;
DevMode: PDeviceMode; // A Pointer to a TDeviceMode structure
begin
{ First obtain a handle to the TPrinter's DeviceMode structure }
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
{ If DeviceHandle is still 0, then the driver was not loaded. Set
the printer index to force the printer driver to load making the
handle available }
if DeviceHandle = 0 then
begin
Printer.PrinterIndex := Printer.PrinterIndex;
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
end;
{ If DeviceHandle is still 0, then an error has occurred. Otherwise,
use GlobalLock() to get a pointer to the TDeviceMode structure }
if DeviceHandle = 0 then
Raise Exception.Create('Could Not Initialize TDeviceMode structure')
else
DevMode := GlobalLock(DeviceHandle);
with DevMode^ do
begin
dmFields := DM_DEFAULTSOURCE;
dmDefaultSource := ToBin;
end;
if not DeviceHandle = 0 then
GlobalUnlock(DeviceHandle);
end;
Then in your printing code do this:
CODE
begin
ChangePrinterBin(3); // call the procedure above and select your bin
// I use bin 3 (1 = manual feed, 2 is top, 3 is bottom)
RE_report.OpenFile(sRepName); // my rich edit report
RE_Report.Print('Performance report'); // report title
MessageDlg('Printed!', mtInformation,[mbOk], 0);
end;
Hope that helps!
Chris
RE: Printer Controls - Set Tray
Thanks & have a star!
Leslie
RE: Printer Controls - Set Tray
CODE
interface
uses Printers;
procedure ChangePrinterBin(ToBin: Integer);
implementation
procedure ChangePrinterBin(ToBin: Integer);
//from : http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt
var
ADevice, ADriver, APort: array [0..255] of Char;
DeviceHandle: THandle;
DevMode: PDeviceMode; // A Pointer to a TDeviceMode structure
begin
{ First obtain a handle to the TPrinter's DeviceMode structure }
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
{ If DeviceHandle is still 0, then the driver was not loaded. Set
the printer index to force the printer driver to load making the
handle available }
if DeviceHandle = 0 then
begin
Printer.PrinterIndex := Printer.PrinterIndex;
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
end;
{ If DeviceHandle is still 0, then an error has occurred. Otherwise,
use GlobalLock() to get a pointer to the TDeviceMode structure }
if DeviceHandle = 0 then
Raise Exception.Create('Could Not Initialize TDeviceMode structure')
else
DevMode := GlobalLock(DeviceHandle);
with DevMode^ do
begin
dmFields := DM_DEFAULTSOURCE;
dmDefaultSource := ToBin;
end;
if not DeviceHandle = 0 then
GlobalUnlock(DeviceHandle);
end;
end.
When I try to compile I get the following errors:
Undeclared Identifier: 'PDeviceMode'
Undeclared Identifier: 'Exception'
Undeclared Identifier: 'Global Lock'
Pointer Type Required
Undeclared Identifier: 'dmFields'
Undeclared Identifier: 'DM_DEFAULTSOURCE'
Undeclared Identifier: 'dmDefaultSource'
Undeclared Identifier: 'Global Unlock'
I've searched and can't see what I'm not including that is necessary for the PDeviceMode; I've never worked with pointers, so I'm clueless how to resolve the 'Pointer Type Required' error (although I'm searching on it!!).
Any suggestions?
Thanks!
leslie
RE: Printer Controls - Set Tray
Didn't help the other issues though!
RE: Printer Controls - Set Tray
Thanks!
les
RE: Printer Controls - Set Tray
But it might be possible you are using a rather old Delphi version? I just checked with D7 & D5 and both know about the Printers unit, and Windows.pas, and could compile just fine...
HTH
TonHu
RE: Printer Controls - Set Tray
I added 'Windows' to the uses clause and I'm down to just two errors!
CODE
Incompatible Types: '_devicemodeA' and 'Pointer'
Thanks!
les
RE: Printer Controls - Set Tray
Apologies for not giving you all the units earlier (as well as not replying sooner - I'm on the other side of the planet!).
Here is my uses list:
CODE
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Printers,
Dialogs, StdCtrls, Grids, DBGrids, DateUtils, DB, ADODB, Buttons, ComCtrls,
FormatText {my own unit], sort {my own unit},
abcrched , abclabel, abcdlg {abc tools units I use};
Specifically, to fix the Exception error, you need the SysUtils unit.
The _deviceModeA is in the Windows unit, and defines the class for PDevicemode. You should put back the ^ to fix this as _deviceModeA is a pointer type - so you are getting type mismatches.
ie, DevMode^.dmFields and DevMode^.dmDefaultSource are pointers to elements in the DevMode structure. Check out the Win32SDK help for all the elements and constants available to you.
Hope that helps finalise it.
Chris
RE: Printer Controls - Set Tray
Here's what I've currently got:
CODE
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Printers,
Dialogs, StdCtrls, Grids, DBGrids, DateUtils, DB, ADODB, Buttons, ComCtrls;
procedure ChangePrinterBin(ToBin: Integer);
implementation
procedure ChangePrinterBin(ToBin: Integer);
//from : http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt
var
ADevice, ADriver, APort: array [0..255] of Char;
DeviceHandle: THandle;
DevMode: TDeviceMode; // A Pointer to a TDeviceMode structure
begin
{ First obtain a handle to the TPrinter's DeviceMode structure }
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
{ If DeviceHandle is still 0, then the driver was not loaded. Set
the printer index to force the printer driver to load making the
handle available }
if DeviceHandle = 0 then
begin
Printer.PrinterIndex := Printer.PrinterIndex;
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
end;
{ If DeviceHandle is still 0, then an error has occurred. Otherwise,
use GlobalLock() to get a pointer to the TDeviceMode structure }
if DeviceHandle = 0 then
Raise Exception.Create('Could Not Initialize TDeviceMode structure')
else
DevMode^ := GlobalLock(DeviceHandle);
with DevMode^ do
begin
dmFields := DM_DEFAULTSOURCE;
dmDefaultSource := ToBin;
end;
if not DeviceHandle = 0 then
GlobalUnlock(DeviceHandle);
end;
end.
Okay there are two of the ^ & ^. The ^ one is the original location.
If I try to compile the code with BOTH ^ & ^, I get the following error messages:
CODE
(36) Pointer type required
(38) Undeclared Identifier: 'dmFields'
(39) Undeclared Identifier: 'dmDefaultSource'
If I try to compile the code with just the ^, I get the following error messages:
CODE
If I try to compile the code with just the ^, I get the following error messages:
CODE
(36) Pointer type required
(38) Undeclared Identifier: 'dmFields'
(39) Undeclared Identifier: 'dmDefaultSource'
Any more suggestions??
thanks!
les
RE: Printer Controls - Set Tray
I think I've found the error -
First, you've changed the DevMode : PDeviceMode var declaration to DevMode:TDeviceMode - this won't work.
It should be:
CODE
ADevice, ADriver, APort: array [0..255] of Char;
DeviceHandle: THandle;
DevMode: PDeviceMode; // A Pointer to a TDeviceMode structure
Next, take out the ^ in DevMode^ := GlobalLock(DeviceHandle);
I've created a small test app and updated the unit to allow listing the bins (see the notes after the code), and it works fine. You can shorten your uses clause as well. So your final unit should be:
CODE
interface
uses Windows, Messages, SysUtils, Variants, Classes, Printers, WinSpool;
type // these are needed for reading the Windows and WinSpool units data structures - leave the ^ in!!!!
LPBYTE = ^byte;
PPr_info_2 = ^printer_info_2;
TBinNames = array[ 0..99,0..23] of char;
PTBinNames = ^TBinNames;
var
FPrinter : TPrinter;
FDevice : PChar;
FDriver : PChar;
FPort : PChar;
BinNames : PTBinNames;
BinCodes : array of word;
DeviceMode : THandle;
DeviceMode2 : LPBYTE;
DevMode : PDeviceMode;
Driver_info_2 : pDriverinfo2;
pr_info_2 : PPr_info_2;
Retrieved : dword;
hPrinter : THandle;
ret2 : pdword;
CapBuffer : PChar;
BinInfo : tStringList;
NumCaps : integer;
procedure ChangePrinterBin(ToBin: Integer);
procedure GetDriverInfo(Sender : TObject; BinInfo : tStringList);
implementation
procedure ChangePrinterBin(ToBin: Integer);
//from : http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt
var
ADevice, ADriver, APort: array [0..255] of Char;
DeviceHandle: THandle;
DevMode: PDeviceMode; // A Pointer to a TDeviceMode structure
begin
{ First obtain a handle to the TPrinter's DeviceMode structure }
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
{ If DeviceHandle is still 0, then the driver was not loaded. Set
the printer index to force the printer driver to load making the
handle available }
if DeviceHandle = 0 then
begin
Printer.PrinterIndex := Printer.PrinterIndex;
Printer.GetPrinter(ADevice, ADriver, APort, DeviceHandle);
end;
{ If DeviceHandle is still 0, then an error has occurred. Otherwise,
use GlobalLock() to get a pointer to the TDeviceMode structure }
if DeviceHandle = 0 then
Raise Exception.Create('Could Not Initialize TDeviceMode structure')
else
DevMode := GlobalLock(DeviceHandle);
with DevMode^ do
begin
dmFields := DM_DEFAULTSOURCE;
dmDefaultSource := ToBin;
end;
if not DeviceHandle = 0 then
GlobalUnlock(DeviceHandle);
end;
procedure MemAllocations_Open(Sender: TObject); // sets aside memory for storing the information
begin
GetMem(CapBuffer, 10000);
GetMem(FDevice, 1000);
GetMem(FDriver, 1000);
GetMem(FPort, 1000);
getmem( ret2, 1000 );
GetMem( binnames, 2400);
fprinter := tprinter.Create;
fprinter.PrinterIndex := -1;
end;
procedure MemAllocations_close(Sender: TObject); // clears memory when you finish
begin
fPrinter.free;
FreeMem(CapBuffer, 10000);
FreeMem(FDevice, 1000);
FreeMem(FDriver, 1000);
FreeMem(FPort, 1000);
FreeMem( ret2, 1000 );
FreeMem( binnames, 2400);
SetLength( BinCodes, 0 );
end;
procedure GetDriverInfo(Sender : TObject; BinInfo : tStringList); // call this to get the driver information
var
I: Integer;
begin
MemAllocations_Open(Sender);
if not assigned(fPrinter) then exit;
// this call returns the FDevice string of the selected printer
Printer.GetPrinter(FDevice, FDriver, FPort, DeviceMode);
DevMode := GlobalLock(DeviceMode);
//----------------------------------------------------
GetMem(Driver_info_2, 1000);
try
OpenPrinter(FDevice, hPrinter, nil);
try
GetPrinterDriver(hPrinter, nil, 2, Driver_info_2, 1000, Retrieved);
except
end;
except
end;
//-------------------------------------------------------}
DeviceCapabilities(FDevice, FPort, DC_BINS, CapBuffer, nil);
// Bin names
NumCaps := DeviceCapabilities(FDevice, FPort, DC_BINNAMES, CapBuffer, nil);
copymemory( pointer(binnames), capbuffer, 2400);
SetLength(BinCodes, NumCaps);
Fillchar(Pointer(BinCodes)^, NumCaps * Sizeof(word), #0);
// Bin codes
NumCaps := DeviceCapabilities(FDevice, FPort, DC_BINS, PChar(BinCodes), nil);
// write valid bin codes to the string list
for I := 0 to NumCaps - 1 do
begin
if trim(binnames[i]) <> '' then begin
BinInfo.Add(binnames[i] + format( ' : Code= %d', [BinCodes[I]]));
end;
end;
// clean it all up
FreeMem(Driver_info_2, 255);
globalunlock( devicemode);
MemAllocations_Close(Sender);
end;
end.
Finally, I may have given you some erroneous information about the printer bin selection. This varies with the printer and the number of trays, and needs to be retrieved.
If you use the unit as I've created it, it will retrieve information about the printer bin capabilities (found most of this code on the web - can't remember the source - its cleaned up for this task).
eg My HP 2200TN has 3 bins, has the following bin codes:
Automatically Select : Code= 15
Printer Auto Select : Code= 259
Tray 1 : Code= 1
Manual Feed (Tray 1) : Code= 4
Tray 2 : Code= 258
Tray 3 : Code= 257
To use this function, in your printing unit, take out what you need from my testing unit (only had a button, a richedit memo for testing and list box for displaying what was found):
CODE
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ComCtrls, SetPrinterTray;
type
TF_main = class(TForm)
Button1: TButton;
RE_report: TRichEdit;
ListBox1: TListBox;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
F_main: TF_main;
implementation
{$R *.dfm}
procedure TF_main.Button1Click(Sender: TObject);
begin
ChangePrinterBin(257); // this is hardwired, but you could create a list
RE_Report.Print('Performance report'); // report title
MessageDlg('Printed!', mtInformation,[mbOk], 0);
end;
procedure TF_main.FormCreate(Sender: TObject); // easiest if you do this on form creation
begin
BinInfo := TStringList.Create; // create a string list
BinInfo.Clear; // clear the string list
GetDriverInfo(Sender, BinInfo); // this will pick up the DEFAULT printer on the system
ListBox1.Items := BinInfo; // I created a list box here and populated it with the BinInfo collected
end;
procedure TF_main.FormClose(Sender: TObject; var Action: TCloseAction);
begin
BinInfo.Free; // make sure you free the list when your close the application!
end;
In your case wanting to use Tray 6, you simply have to search for the tray in the string list and choose the number on the end as the printer ID
I hope that fixes your problems.
Cheers,
Chris
RE: Printer Controls - Set Tray
I'll try to play around with this today (although you won't know until tomorrow! - where in the world are you anyway? My guess - Australia/New Zealand), but I'm trying to finish up a project and probably won't have time until early next week.
I'll definitely post back if I have any problems!
leslie
RE: Printer Controls - Set Tray
I appreciate the feedback. Glad to help - which is why we are part of this forum!
As for my location - you got it - Adelaide, Australia.
Hope it all works out, and have a great weekend.
Cheers,
Chris