×
INTELLIGENT WORK FORUMS
FOR COMPUTER PROFESSIONALS

Contact US

Log In

Come Join Us!

Are you a
Computer / IT professional?
Join Tek-Tips Forums!
  • Talk With Other Members
  • Be Notified Of Responses
    To Your Posts
  • Keyword Search
  • One-Click Access To Your
    Favorite Forums
  • Automated Signatures
    On Your Posts
  • Best Of All, It's Free!

*Tek-Tips's functionality depends on members receiving e-mail. By joining you are opting in to receive e-mail.

Posting Guidelines

Promoting, selling, recruiting, coursework and thesis posting is forbidden.

Students Click Here

Printer Controls - Set Tray
3

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!

Leslie

Anything worth doing is a lot more difficult than it's worth - Unknown Induhvidual

RE: Printer Controls - Set Tray

2
Hi Leslie!

I've used this successfully to change printer trays:

CODE

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;

Then in your printing code do this:

CODE

procedure TF_main.B_PrintClick(Sender: TObject);

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

(OP)
I will try this out today!!  I'm so excited that this is possible.

Thanks & have a star!

Leslie

RE: Printer Controls - Set Tray

(OP)
Ok, having slight problems.  Here's what I've got:

CODE

unit SetPrinterTray;

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

(OP)
Ok found the fix to the pointer error, just had to remove the '^'!!

Didn't help the other issues though!

RE: Printer Controls - Set Tray

(OP)
Anybody?  Still trying to figure out why I get the undeclared identifier for the PDeviceMode, etc.

Thanks!

les

RE: Printer Controls - Set Tray

You should put $(DELPHI)\source\rtl\win in your environment (Delphi Tools/Environment options.../Library/Browsing paths... that's where windows.pas is kept, and where these types are defined.

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

(OP)
progress!

I added 'Windows' to the uses clause and I'm down to just two errors!

CODE

Undeclared Identifier:  'Exception'
Incompatible Types: '_devicemodeA' and 'Pointer'
I'm not sure what the '_devicemodeA' is all about????

Thanks!

les

RE: Printer Controls - Set Tray

Hi Leslie!

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

uses
  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

(OP)
Thanks for the uses clause!  Unfortunately it didn't help.  :(

Here's what I've currently got:

CODE

unit SetPrinterTray;

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.
First, like I said above, I have no experience with pointers and I'm fairly confused on what this code is exactly doing.  So, I'm trying all different combinations of things I can think of to get this to work.

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

(34) Pointer type required
(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

(34) Pointer type required

If I try to compile the code with just the ^, I get the following error messages:

CODE

(34) Incompatible Types: '_devicemodeA' and 'Pointer'
(36) Pointer type required
(38) Undeclared Identifier:  'dmFields'
(39) Undeclared Identifier:  'dmDefaultSource'

Any more suggestions??

thanks!

les

RE: Printer Controls - Set Tray

G'day Les!

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

var
  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

unit SetPrinterTray;

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

unit PrnTesting;

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

(OP)
Thank you so much!  I had forgotten that I changed that variable (when I was trying so many things!!).

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

G'day Les!

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

Red Flag This Post

Please let us know here why this post is inappropriate. Reasons such as off-topic, duplicates, flames, illegal, vulgar, or students posting their homework.

Red Flag Submitted

Thank you for helping keep Tek-Tips Forums free from inappropriate posts.
The Tek-Tips staff will check this out and take appropriate action.

Reply To This Thread

Posting in the Tek-Tips forums is a member-only feature.

Click Here to join Tek-Tips and talk with other members! Already a Member? Login

Close Box

Join Tek-Tips® Today!

Join your peers on the Internet's largest technical computer professional community.
It's easy to join and it's free.

Here's Why Members Love Tek-Tips Forums:

Register now while it's still free!

Already a member? Close this window and log in.

Join Us             Close