Solved

Printer API -different with Windows 98 ?

Posted on 1999-01-26
63
259 Views
Last Modified: 2010-04-06
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
Comment
Question by:sharons
  • 38
  • 12
  • 10
  • +3
63 Comments
 
LVL 20

Expert Comment

by:Madshi
ID: 1363451
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
 

Author Comment

by:sharons
ID: 1363452
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363453
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
 
LVL 2

Expert Comment

by:cartti
ID: 1363454
Häh?
0
 
LVL 1

Expert Comment

by:Cooler
ID: 1363455
Testing Testing 1 2 3
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363456
Cooler's test comment
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363457
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
 
LVL 10

Expert Comment

by:viktornet
ID: 1363458
What's wrong with this?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363459
What's wrong with this thing?
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363460
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363461
Dummy comment just to move my "real" comment to the visible area...   :-)
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363462
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363463
sharons, be careful, you have to put 2 dummy comments after your real comment, otherwise your new comment won't be seen...   :-(
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363464
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363465
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363466
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
 
LVL 10

Expert Comment

by:viktornet
ID: 1363467
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363468
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363469
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363470
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363471
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363472
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
 
LVL 10

Expert Comment

by:viktornet
ID: 1363473
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363474
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363475
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363476
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363477
dummy
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363478
Maybe I should try reinstalling Win98 once again :(

-Viktor
--Ivanov
0
 
LVL 10

Expert Comment

by:viktornet
ID: 1363479
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363480
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363481
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
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 
LVL 20

Expert Comment

by:Madshi
ID: 1363482
dummy
0
 

Expert Comment

by:earthworm
ID: 1363483
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363484
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363485
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363486
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363487
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363488
dummy
0
 

Author Comment

by:sharons
ID: 1363489
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363490
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363491
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363492
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363493
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363494
dummy
0
 

Author Comment

by:sharons
ID: 1363495
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363496
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363497
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363498
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363499
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363500
dummy
0
 

Author Comment

by:sharons
ID: 1363501
dummy
0
 

Author Comment

by:sharons
ID: 1363502
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
 

Author Comment

by:sharons
ID: 1363503
dummy
0
 
LVL 20

Accepted Solution

by:
Madshi earned 290 total points
ID: 1363504
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363505
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363506
dummy
0
 

Author Comment

by:sharons
ID: 1363507
dummy
0
 

Author Comment

by:sharons
ID: 1363508
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
 

Author Comment

by:sharons
ID: 1363509
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363510
dummy
0
 

Author Comment

by:sharons
ID: 1363511
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1363512
dummy
0
 
LVL 20

Expert Comment

by:Madshi
ID: 1363513
dummy
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
This tutorial gives a high-level tour of the interface of Marketo (a marketing automation tool to help businesses track and engage prospective customers and drive them to purchase). You will see the main areas including Marketing Activities, Design …
Learn how to create flexible layouts using relative units in CSS.  New relative units added in CSS3 include vw(viewports width), vh(viewports height), vmin(minimum of viewports height and width), and vmax (maximum of viewports height and width).

920 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now