Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations John Tel on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Printer Controls - Set Tray 3

Status
Not open for further replies.

lespaul

Programmer
Feb 4, 2002
7,083
US
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
 
Hi Leslie!

I've used this successfully to change printer trays:
Code:
procedure ChangePrinterBin(ToBin: Integer);
//from : [URL unfurl="true"]http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt[/URL]
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 ;-)
 
I will try this out today!! I'm so excited that this is possible.

Thanks & have a star!

Leslie
 
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 : [URL unfurl="true"]http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt[/URL]
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
 
Ok found the fix to the pointer error, just had to remove the '^'!!

Didn't help the other issues though!
 
Anybody? Still trying to figure out why I get the undeclared identifier for the PDeviceMode, etc.

Thanks!

les
 
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
 
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
 
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:
[b]uses[/b]
  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 [tt]Exception[/tt] error, you need the [tt]SysUtils[/tt] unit.

The [tt]_deviceModeA[/tt] is in the [tt]Windows[/tt] unit, and defines the class for [tt]PDevicemode[/tt]. You should put back the ^ to fix this as _deviceModeA is a pointer type - so you are getting type mismatches.
ie, [tt]DevMode^.dmFields[/tt] and [tt]DevMode^.dmDefaultSource[/tt] 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 ;-)
 
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 : [URL unfurl="true"]http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt[/URL]
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[COLOR=red][b]^[/b][/color] := GlobalLock(DeviceHandle);

    with DevMode[COLOR=blue][b]^[/b][/color] 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
 
G'day Les!

I think I've found the error -
First, you've changed the [tt]DevMode : [blue]PDeviceMode[/blue][/tt] var declaration to [tt]DevMode:[red]TDeviceMode[/red][/tt] - this won't work.
It should be:
Code:
var
  ADevice, ADriver, APort: array [0..255] of Char;
  DeviceHandle: THandle;
  DevMode: [blue]PDeviceMode[/blue]; // A Pointer to a TDeviceMode structure

Next, take out the [red][tt]^[/tt][/red] in [tt]DevMode[red]^[/red] := GlobalLock(DeviceHandle);[/tt]

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 [tt]uses[/tt] 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 : [URL unfurl="true"]http://www.efg2.com/Lab/Library/UseNet/1999/1102.txt[/URL]
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:
[tt]
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
[/tt]

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 [pc2]
 
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!

[2thumbsup]

leslie
 
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 [pc2]
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top