Solved

Printer API -different with Windows 98 ?

Posted on 1999-01-26
63
258 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
Comment Utility
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
Comment Utility
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
Comment Utility
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
Comment Utility
Häh?
0
 
LVL 1

Expert Comment

by:Cooler
Comment Utility
Testing Testing 1 2 3
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
Cooler's test comment
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
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
Comment Utility
What's wrong with this?
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
What's wrong with this thing?
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
Dummy comment just to move my "real" comment to the visible area...   :-)
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
dummy
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
Maybe I should try reinstalling Win98 once again :(

-Viktor
--Ivanov
0
 
LVL 10

Expert Comment

by:viktornet
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 

Expert Comment

by:earthworm
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Accepted Solution

by:
Madshi earned 290 total points
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
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
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0
 

Author Comment

by:sharons
Comment Utility
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
Comment Utility
dummy
0
 
LVL 20

Expert Comment

by:Madshi
Comment Utility
dummy
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

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…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

743 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

17 Experts available now in Live!

Get 1:1 Help Now