Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 280
  • Last Modified:

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 ?




0
sharons
Asked:
sharons
  • 38
  • 12
  • 10
  • +3
1 Solution
 
MadshiCommented:
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.
0
 
sharonsAuthor Commented:
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.
0
 
MadshiCommented:
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 ?


0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
carttiCommented:
Häh?
0
 
CoolerCommented:
Testing Testing 1 2 3
0
 
viktornetCommented:
Cooler's test comment
0
 
viktornetCommented:
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
0
 
viktornetCommented:
What's wrong with this?
0
 
MadshiCommented:
What's wrong with this thing?
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
Dummy comment just to move my "real" comment to the visible area...   :-)
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
sharons, be careful, you have to put 2 dummy comments after your real comment, otherwise your new comment won't be seen...   :-(
0
 
MadshiCommented:
dummy
0
 
viktornetCommented:
dummy
0
 
viktornetCommented:
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
0
 
viktornetCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
viktornetCommented:
dummy
0
 
viktornetCommented:
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
0
 
viktornetCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
viktornetCommented:
dummy
0
 
viktornetCommented:
Maybe I should try reinstalling Win98 once again :(

-Viktor
--Ivanov
0
 
viktornetCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
earthwormCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
sharonsAuthor Commented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
sharonsAuthor Commented:
dummy
0
 
MadshiCommented:
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.


0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
sharonsAuthor Commented:
dummy
0
 
sharonsAuthor Commented:
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.
0
 
sharonsAuthor Commented:
dummy
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
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.
0
 
MadshiCommented:
dummy
0
 
sharonsAuthor Commented:
dummy
0
 
sharonsAuthor Commented:
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.

0
 
sharonsAuthor Commented:
dummy
0
 
MadshiCommented:
dummy
0
 
sharonsAuthor Commented:
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.
0
 
MadshiCommented:
dummy
0
 
MadshiCommented:
dummy
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 38
  • 12
  • 10
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now