0

I have the following function below that gathers the document properties of a PDF that I am printing. For some reason, in Delphi 7 (running XP), this works great...however, when I try to recompile with Delphi XE using Windows 7, the function always seems to exit failing...dwRet = IDOK!

I noticed that my dwNeeded object in Delphi 7 was 7332, and in XE it is 4294967295!!

Any idea how I can quickly fix this?

Function TPrintPDF.GetPrinterDevMode ( pDevice: PChar ): PDevMode;
Var
  pDevModeVar : PDevMode;
  pDevModeVar2 : PDevMode;
  dwNeeded : DWord;
  dwRet : DWord;
Begin

  { Start by opening the printer }
  If (Not OpenPrinter (pDevice, PrinterHandle, Nil))
    Then Result := Nil;

  { Step 1: Allocate a buffer of the correct size }
  dwNeeded := DocumentProperties (0,
                         PrinterHandle, { Handle to our printer }
                         pDevice, { Name of the printer }
                         pDevModevar^, { Asking for size, so these are not used }
                         pDevModeVar^,
                         0); { Zero returns buffer size }

  GetMem (pDevModeVar, dwNeeded);

  { Step 2: Get the default DevMode for the printer }
  dwRet := DocumentProperties (0,
                 PrinterHandle,
                 pDevice,
                 pDevModeVar^, { The address of the buffer to fill }
                 pDevModeVar2^, { Not using the input buffer }
                 DM_OUT_BUFFER); { Have the output buffer filled }

  { If failure, cleanup and return failure }
  If (dwRet <> IDOK) Then Begin
    FreeMem (pDevModeVar);
    ClosePrinter (PrinterHandle);
    Result := Nil;
  End;

  { Finished with the printer }
  ClosePrinter (PrinterHandle);

  { Return the DevMode structure }
  Result := pDevModeVar;

End; { GetPrinterDevMode Function }
Ken White
  • 123,280
  • 14
  • 225
  • 444
Vortex
  • 105
  • 10
  • `dwNeeded` should be declared as `LONG`. It is signed. Negative value means the function failed. That's exactly what happened to you. You don't check for errors when you call `DocumentProperties`. Quite possibly the Delphi header translation for `DocumentProperties` is bogus. – David Heffernan Jul 31 '12 at 18:35
  • So I could use an Int64 instead of a DWord for dwNeeded? – Vortex Jul 31 '12 at 19:04
  • 1
    No, like I said, it should be declared as `LONG`. I also wonder whether it is wise to pass garbage to the 4th and 5th params. – David Heffernan Jul 31 '12 at 19:06
  • [DCC Error] PrintPDF.pas(583): E2003 Undeclared identifier: 'Long' – Vortex Jul 31 '12 at 19:10
  • Maybe something like this instead? { Step 1: Allocate a buffer of the correct size } dwNeeded := DocumentProperties (0, PrinterHandle, { Handle to our printer } pDevice, { Name of the printer } Nil, { Asking for size, so these are not used } Nil, 0); { Zero returns buffer size } – Vortex Jul 31 '12 at 19:10
  • `LONG` is declared in Windows.pas in my XE2. If XE doesn't have it then use `Integer`. Not sure if you can pass nil for both of them. Try passing pointers to real DEVMODE structs first. – David Heffernan Jul 31 '12 at 19:10
  • By changing it to dwNeeded := DocumentProperties (0,PrinterHandle,pDevice,Nil,Nil,0); it worked! I kept it at Int64. – Vortex Jul 31 '12 at 19:13
  • 1
    Don't use `Int64`! Use `LONG` from `Windows`. Or `Integer` if you must. You might as well try and be accurate. Also your error handling is completely and utterly broken in this routine. – David Heffernan Jul 31 '12 at 19:15
  • 1
    This kind of "it works", go to production, no matter if the Delphi firm match the API, is what 90% of the times makes it stop working when upgrading the Development Environment or the Operating System. As suggested by @David, you might be accurate!!! – jachguate Jul 31 '12 at 19:19

2 Answers2

2

Here are the problems that I can see with your code:

  1. The return value of DocumentProperties is a signed 32 bit integer. It's declared as LONG. A negative value means an error occurred and that's what's happening to you. Only you don't see the negative value because you've stuffed the value into an unsigned integer. Unfortunately XE fails to declare LONG. So change your code to use Integer instead.
  2. You don't check for errors when DocumentProperties returns. If an error occurs, a negative value is returned. Make sure you check for that.
  3. You are passing random garbage in the 4th and 5th parameters to DocumentProperties. I suspect that you can pass nil for both parameters the first time that you call DocumentProperties. You can certainly pass nil for the 5th parameter both times you call the function since you never set DM_IN_BUFFER.
  4. When errors occur you set Result to nil, but you continue executing the rest of the function. Don't do that. Call exit to break out of the function. Assigning to Result does not terminate execution in the way that return does in C-like languages does.
  5. Use a try/finally block to ensure that you call CloseHandle. That allows you to write CloseHandle once only.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
0

Here's the solution that David suggested...Thanks David!

{ ---------------------------------------------------------------------------- }

Function TPrintPDF.GetPrinterDevMode                           (         pDevice: PChar                ): PDevMode;


Var
   pDevModeVar       : PDevMode;
   pDevModeVar2      : PDevMode;
   dwNeeded          : Long64;
   dwRet             : Long64;


Begin

  Result := Nil;

  { Start by opening the printer }
  If (OpenPrinter (pDevice, PrinterHandle, Nil)) Then Begin

    Try

      { Step 1: Allocate a buffer of the correct size }
      dwNeeded := DocumentProperties (0,
                                      PrinterHandle,  { Handle to our printer }
                                      pDevice,        { Name of the printer   }
                                      Nil,            { Asking for size, so these are not used }
                                      Nil,
                                      0);             { Zero returns buffer size }

      { Exit if this fails }
      If (dwNeeded < 0)
        Then Exit;

      GetMem (pDevModeVar, dwNeeded);


      { Step 2: Get the default DevMode for the printer }
      dwRet := DocumentProperties (0,
                                   PrinterHandle,
                                   pDevice,
                                   pDevModeVar^,      { The address of the buffer to fill }
                                   pDevModeVar2^,     { Not using the input buffer }
                                   DM_OUT_BUFFER);    { Have the output buffer filled }


      { If failure, cleanup and return failure }
      If (dwRet <> IDOK) Then Begin
        FreeMem (pDevModeVar);
        ClosePrinter (PrinterHandle);
        Result := Nil;
      End;


    { Finished with the printer }
    Finally
      ClosePrinter (PrinterHandle);
    End; { Try }

    { Return the DevMode structure }
    Result := pDevModeVar;

  End; { If we could open the printer }


End; { GetPrinterDevMode Function }
Vortex
  • 105
  • 10
  • 1
    That is most definitely not what I suggested! I suggested using `LONG` and not passing random garbage like `pDevModeVar2^`. And I also suggested handling errors properly. You don't check for errors when you call `DocumentProperties`. And when you do check for errors you don't do it properly. – David Heffernan Jul 31 '12 at 19:17
  • XE doesn't support the LONG type. So I'm stuck with Int64's. As for the Error Handling...what to you suggest? I'm fairly new to this level of programming. – Vortex Jul 31 '12 at 19:25
  • Indeed I can't find LONG in windows.pas also. Use 'Longint', that's how 'DocumentProperties' is declared in 'winspool.pas'. Or, 'Integer' as suggested.. – Sertac Akyuz Jul 31 '12 at 19:41
  • @Sertac I guess I started XE2 by mistake. It's declared there. – David Heffernan Jul 31 '12 at 19:48
  • @David - Indeed. They should be changing icons more frequently.. :) – Sertac Akyuz Jul 31 '12 at 19:54