Link to home
Start Free TrialLog in
Avatar of sharons
sharons

asked on

Printer API -different with Windows 98 ?

I develop an application under Win NT but with the intention of using it under Win 98 as well. I make use of printer APIs such as

OpenPrinter(PChar(Printer.Printers.Strings[Printer.PrinterIndex]), vPrinterHandle, nil)

This command works well under WinNT and now it returns false under Win 98.

Same thing with

      if EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned) then ...

The values in JobInfo are also wrong.

Again, this code works very well in Win NT. Any suggestions ?




Avatar of Madshi
Madshi

Hmm. I'm not using Delphi's Printer object at all. I'm working with pure winAPI in this case and it works well in 95/NT and 98. I can't see any errors in your OpenPrinter statement. Perhaps you should check whether Printer.Printers.Strings[Printer.PrinterIndex] really gives you the exact name of the printer (CompareStr, not CompareText!).
If it does give you the right value and you nevertheless get a false as result, please tell me the GetLastError number.

Regards, Madshi.
Avatar of sharons

ASKER

Hmm. I'm not using Delphi's Printer object at all. I'm working with pure winAPI in this case and it works well in 95/NT and 98. I can't see any errors in your OpenPrinter statement. Perhaps you should check whether Printer.Printers.Strings[Printer.PrinterIndex] really gives you the exact name of the printer (CompareStr, not CompareText!).
If it does give you the right value and you nevertheless get a false as result, please tell me the GetLastError number.

Regards, Madshi.
You are right, it has to do with the value retrieved by Printer.Printers.Strings[Printer.PrinterIndex]. My printer's name is Brother HL-630 but the string in printers is Bother HL-630 on LPT1:.

So that's one problem I have to solve. Then I hardcoded the printer's name and it opens it fine but now I am having problems with the following:

    if OpenPrinter('Brother HL-630', vPrinterHandle, nil) then
//    if OpenPrinter(PChar(Printer.Printers.Strings[Printer.PrinterIndex]), vPrinterHandle, nil) then
    try
      GetMem(vJobInfo, 1000);

      // Get job info.
      if EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned) then
      begin

        // If job is still spooling we have to keep reading the total number
        // of pages since it goes up as the job is spooling.
        while vJobInfo^.Status = JOB_STATUS_SPOOLING do
          EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned);

The last loop gets into an infinite loop. I guess Win98 is very different. Any suggestions ?


Häh?
Testing Testing 1 2 3
Cooler's test comment
hello sharons... Read your Win32.hlp file and see what it says about those APIs... In my Win32.hlp it says that the funciton is used under Win95 and and WinNT.. Check out yours to see if you can use that under Win98... if not see which is used instead...

-Viktor
--Ivanov
What's wrong with this?
What's wrong with this thing?
Hmmm. Is this question alright again?

Hi Viktor, if the win32.hlp file says, it works under win95, is usually works under win98 as well. There's no API (that I know) that works under win95 and NOT under win98.

Sharons, some hints. First of all, are you sure that Msg.JobsLeft is correct? I mean, what if the number of jobs decreases from 2 to 1. The Msg.JobsLeft variable seems to remain unchanged. So I guess that could be problem.
Another thing: You MUST check the return value of the EnumJobs function. Because if this function fails the Buffer won't be changed and the status will stay forever. So I think your loop doesn't end, because the EnumJobs function fails from the x-th call on.

Regards, Madshi.
Dummy comment just to move my "real" comment to the visible area...   :-)
dummy
sharons, be careful, you have to put 2 dummy comments after your real comment, otherwise your new comment won't be seen...   :-(
dummy
dummy
Madshi, there are plenty of APIs that don't work with Win98 because of the changes made to Win98...

when using WM_CLOSE and you send the message to a Window that is a folder then it won't close as in Win95 it closes alright...

here is what I mean...

SendMessage(FindWindow(nil, 'C:\windows\desktop\myfolder'), WM_CLOSE, 0, 0);

WM_QUIT is not working, and neither is PostMessage()...

There are lots more that I just don't have the time now to explain.. Also who knows how many there are that people have found and I don't know about,,,

-Viktor
--Ivanov
dummy
dummy
You mean WM_QUIT and PostMessage is not working for folder windows, right? Because I'm using both for other things under win98, and they work quite right.
My win32.hlp doesn't say anything about win98 at all. E.g. look at API "CreateWindowEx". According what you say, it doesn't work for win98...   :-)
No, of course win98 behaves differently than win95 in several things, but the APIs (at least the very most) are compatible. I'm quite sure about that, since we're using the same 3 MB program in win98 now without any significant changes - and without any problems!

Regards, Madshi.
dummy
dummy
I've tried using all combinations with SendMessage(), PostMessage() and WM_CLOSE, WM_QUIT and none of them worked on Win98 that I have,, I've no idea why, but that's what happens.. I've also tried other APIs and some of them didn't work...

-Viktor
--Ivanov
dummy
dummy
If you were right, most win32 programs would not work any longer!!! Perhaps you should try to reinstall your win98?    :-(

I'm really using SendMessage/PostMessage/WM_CLOSE/WM_QUIT every day, and it *really* works...

Regards, Madshi.
dummy
dummy
Maybe I should try reinstalling Win98 once again :(

-Viktor
--Ivanov
dummy
dummy
Hmmm. Try the following code snippet. If refreshes your desktop. If it does not work on your computer, you'll probably have to reinstall...   :-(
If it works, you've done something wrong with your SendMessage/Postmessage calls.

procedure RefreshDesktop;
var c1 : cardinal;
begin
  c1:=FindWindowEx(FindWindowEx(FindWindow('Progman','Program Manager'),0,'SHELLDLL_DefView',''),0,'SysListView32','');
  PostMessage(c1,WM_KEYDOWN,VK_F5,0); PostMessage(c1,WM_KEYUP,VK_F5,1 shl 31);
end;

Regards, Madshi.
dummy
dummy
I have written some code like this:

{...}
PrinterName:=Printer.Printers.Strings[Printer.PrinterIndex];
if OpenPrinter(PChar(PrinterName),vPrinterHandle,nil) then
   {...}
else
   {..}
{...}

then i run it
the printername get from Printer.Printers.Strings is 'HP LaserJet 4L on \\Yffwq\hp' (it's a network printer on the other computer) and the OpenPrinter function return FALSE. so i set the printername to 'HP LaserJet 4L' what same as the printer name i can see in the control panel, and now the OpenPrinter function return TRUE.

Now you see? the printername get from Printer.Printers.Strings is not always what the OpenPrinter function needs...  :-)

And so ,i suggest you not to uses the printer through Windows API. Delphi have provided a Printer object which is easy to use.
you can:
(1)Call the BeginDoc method to open the printer...
(2)uses the Canvas property to print text or draw image...
(3)Call the NewPage method to start a new page...
(4)Call the EndDoc method to close the printer...
It's easy to uses and you can do every thing you want.
dummy
dummy
Hey earthworm,

we already found out what you're telling us here...
Please read the comments the next time before you answer a question. Thank you.

sharons,

any progress with EnumJobs?
Hmmm. To be fair, you should reject earthworm's answer, or am I missing something?

Regards, Madshi.
dummy
Avatar of sharons

ASKER

dummy
I am now using API to get the name of the default printer and to open it to get the handle. My only problem now is when I print from a richedit with the print procedure jobs seem to be stuck in the status SPOOLING. If I print from other applications such as WordPad it's fine any suggestions? Thank you.
dummy
dummy
Hmmm. Could you show us the code where you print from the richedit component and the OpenPrinter/EnumJobs code, too, please.
Without that code I can't say anything about your problem.

So did my comments solve that EnumJobs problem?

Regards, Madshi.
dummy
Avatar of sharons

ASKER

dummy
Hi Madshi,  if you have a minute try the following code. I am running this with Win 98. You might get different results win WinNT.

When printing from the richedit, the status of the job remain SPOOLING causing an infinite loop in the following code. When printing lets say from WordPad while this code is running then it's fine.

Any idea ?  Thank you.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, Winspool;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    function GetDefaultPrinter: String;
    procedure WM_SpoolerStatus(var Msg: TWMSPOOLERSTATUS); message WM_SPOOLERSTATUS;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  RichEdit1.Print('test');
end;

procedure TForm1.WM_SpoolerStatus(var Msg: TWMSPOOLERSTATUS);
const
  // This value is used to always know if a job was just added or just removed
  // from the print queue.
  cLastJobsLeft: Integer = 0;
var
  vJobInfo: PJobInfo1;
  vReturned, vNeeded: DWORD;
  LineCur, Port: PChar;
  PrinterInfo: PChar;
  Count, NumInfo: DWORD;
  vPrinterHandle : THandle;
  vDevMode : PDeviceModeA;
  vPrinterName: String;
begin
  // If a job has been added then get the number of pages of newly spooled job.
  if cLastJobsLeft < Msg.JobsLeft then
  begin
    try
      GetMem(vJobInfo, 1000);

      if OpenPrinter(PChar(GetDefaultPrinter), vPrinterHandle, nil) then
      begin
        // Get job info.
        if EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned) then
        begin
          while (vJobInfo^.Status = JOB_STATUS_SPOOLING) do
          begin
            EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned);
            Application.ProcessMessages;
          end;
          showmessage('Finish spooling...');
        end;
      end;
    finally
      FreeMem(vJobInfo, 1000);
    end;
  end;
end;

function TForm1.GetDefaultPrinter: String;
var
  PrinterInfo: PChar;
  Count, NumInfo: DWORD;
begin
  try
    GetMem(PrinterInfo, Count);
    if EnumPrinters(PRINTER_ENUM_DEFAULT, nil, 5, PByte(PrinterInfo), Count, Count, NumInfo) then
      Result := PPrinterInfo5(PrinterInfo)^.pPrinterName;
  finally
    FreeMem(PrinterInfo, Count);
  end;
end;

end.


dummy
dummy
Again: You still DO NOT ask if the EnumJobs API in the loop worked or didn't work and you MUST do that. Since if it did not work, your loop will be infinite.
So please change your loop like this and tell me what ErrorMessage is shown (I'm 98% sure you'll get an error message).

while (vJobInfo^.Status = JOB_STATUS_SPOOLING) do
          begin
            if not EnumJobs(vPrinterHandle, Msg.JobsLeft - 1, 1, 1, vJobInfo, 1000, vNeeded, vReturned) then begin
              MessageBox(0,pchar(intToStr(GetLastError)),'Error in EnumJobs',0);
              break;
            end;
            Application.ProcessMessages;
          end;

If you get no error message, I'll test the code. But I've not so much time. So please test for an error message first. Thank you...

Regards, Madshi.
dummy
Avatar of sharons

ASKER

dummy
Avatar of sharons

ASKER

The EnumJobs works fine and never reports any error. In fact, when I display the Print manager window that displays the jobs in the queue, this too gets stucked on spooling.

It seems to have something to do with this loop being in the same thread as the printing.  If I start my program and I print from another delphi program with a richedit the print works fine. Thank you.
Avatar of sharons

ASKER

dummy
ASKER CERTIFIED SOLUTION
Avatar of Madshi
Madshi

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ok, I'll test it tomorrow. But I've some ideas right now. Perhaps they help you solve the problem before tomorrow...  :-)

(1) Why do you loop for the job? I mean you get a message if the job STARTS and ENDS. So that's all you need, I think. Or is there a specific reason why you build a loop?

(2) If you really want to do it this way, I think the problem will be this one: Win98 sends this WM_SPOOLERSTATUS to you by using SendMessage. Then Win98 waits for you to return. Afterwards it does the printing. You see the deadlock here? Probably winNT does not wait for the result or prints in another thread. If I'm right with my thoughts here, there are two solutions:
(a) Start a thread that loops for EnumJobs or
(b) try calling "ReplyMessage(0);" in the beginning of the WM_SPOOLERSTATUS message handler. If you do that, Win98 gets the result at once and so doesn't have to wait any longer.

Hmmm. I post this as an answer, before another expert does, since I think, we two will solve this problem together...  :-)

Regards, Madshi.
dummy
Avatar of sharons

ASKER

dummy
Avatar of sharons

ASKER

Thank you madshi. You are right, a response is expected before the printing starts.  

My goal is to count the number of pages printed for each job. I am also hoping to be able to prevent the printing if the number of pages is more than a specified number.

In my previous code I was just accessing one job when using EnumJobs. I am now trying to look at all of them. pJob is a pointer to an array.  vJobInfo.pDocument gives me the name of the first job. How do I get the others. I thought something like   vJobInfo[1].pDocument but that does not work. Any suggestions ?

Do you know if there is an API to delete a print job other than purging all jobs ?

Thank you.

Avatar of sharons

ASKER

dummy
dummy
Avatar of sharons

ASKER

Use something like this:

var vJobInfo2: PJobInfo1;
.
  vJobInfo2:=vJobInfo;
  for i1:=1 to vReturned do begin
    // at this place add something to ask "vJobInfo2"
    vJobInfo2:=pointer(cardinal(vJobInfo2)+sizeOf(TJobInfo1));
  end;

To delete a single job use
  SetJob(jobID,...,JOB_CONTROL_CANCEL);

Regards, Madshi.
dummy
dummy