Jump to content
RTollison

Converting project from Delphi 2006 to Delphi 10.2

Recommended Posts

i have a function that i am unable to solve. Tried array of ansichar but still no luck and i am at a loss on how to resolve. If I need to rewrite it then so be it but i cant find any examples of delphi and devicecapabiliesA.

 

getting errors on the DeviceCapabilitesA function.

 

[dcc32 Error] uiDevMode.pas(64): E2010 Incompatible types: 'PAnsiChar' and 'array[0..255] of Char'
[dcc32 Error] uiDevMode.pas(72): E2010 Incompatible types: 'PAnsiChar' and 'array[0..255] of Char'

 

function PrinterFormExists(FormName: string): boolean;
var
  Device: array [0..255] of char;
  Driver: array [0..255] of char;
  Port: array [0..255] of char;
  DeviceHandle: THandle;
  PaperNames: Pointer;
  i: integer;
  PaperTypes: TStrings;
  PaperCount: integer;
begin
{$R-}// Range checking off.
  Result := False;
  PaperTypes := TStringList.Create;
  try
    // First get the number of paper names available.
    Printer.PrinterIndex := Printer.PrinterIndex;
    Printer.GetPrinter(Device, Driver, Port, DeviceHandle);
    PaperCount := DeviceCapabilitiesA((Device), Port, DC_PAPERNAMES, nil, nil);
    if PaperCount > 0 then
    begin
      { Now allocate the array of paper names. Each paper name is 64 bytes.
        Therefore, allocate PaperCount*64 of memory. }
      GetMem(PaperNames, PaperCount * 64);
      try
        // Retrieve the list of names into the allocated memory block.
        if DeviceCapabilitiesA((Device), Port, DC_PAPERNAMES, PaperNames,
          nil) = -1 then
          raise Exception.Create('DevCap Error');
        // Add the paper names to the appropriate list box.
        for i := 0 to PaperCount - 1 do
          PaperTypes.Add(Uppercase(Trim(StrPas(TPNames(PaperNames^)))));
      finally
        FreeMem(PaperNames, PaperCount * 64);
      end;
      if PaperTypes.IndexOf(UpperCase(FormName)) > -1 then
        Result := True;
    end
  finally
    PaperTypes.Free;
  end;
{$R+}// Range checking back on.
end;

 

Share this post


Link to post

use DeviceCapabilities() instead of DeviceCapabilitiesA(), then you will also find examples

Edited by Attila Kovacs

Share this post


Link to post

If you switch to DeviceCapabilities (which is DeviceCapabilitiesW), take care about memory allocation. 64 bytes is wrong, you will need 64 * SizeOf(Char).

Share this post


Link to post

You need to use StrLCopy instead of StrPas since there's no guarantee that the source string is null terminated. You actually need a StrLPas function but fo some reason they've never implemented that variant.

Also remember that the buffer is a static array[] of Char[0..63] so you need to iterate the outer array somehow.

var Buffer: PChar;
GetMem(Buffer, PaperCount*SizeOf(Char));
try
  var p := Buffer;
  for i := 0 to PaperCount-1 do
  begin
    var PaperName: string;
    SetLength(PaperName, 64);
    StrLCopy(PChar(PaperName), p, 64);
    PaperName := PChar(PaperName); // or SetLength(PaperName, StrLen(PChar(PaperName)))
    ...
    Inc(p, 64);
  end;
finally
  FreeMem(Buffer);
end;

 

  • Like 1

Share this post


Link to post
40 minutes ago, Anders Melander said:

You need to use StrLCopy instead of StrPas since there's no guarantee that the source string is null terminated.

Note that when using StrLCopy(), it copies up to the max length you specify and then adds +1 for a null terminator.  So, to account for the null terminator, you need to either +1 the memory allocation, or -1 the max length to copy.  This is documented behavior.

Quote

You actually need a StrLPas function but fo some reason they've never implemented that variant.

Actually, they did - it is called SetString().

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post

I can't tell from the documentation if SetString copies up to Length characters or always Length characters and it seems to be an intrinsic function so I can't check the source.

Share this post


Link to post
21 minutes ago, Remy Lebeau said:

So, to account for the null terminator, you need to either +1 the memory allocation, or -1 the max length to copy.

In my example there's already room for 65 characters in the destination since Delphi strings have an implicit null terminator.

  • Like 1

Share this post


Link to post

it also copies all the 64 chars so the truncate trick is still needed

SetString(PaperName, p, 64);
PaperName := PChar(PaperName);

Edited by Attila Kovacs

Share this post


Link to post
22 hours ago, Anders Melander said:

I can't tell from the documentation if SetString copies up to Length characters or always Length characters and it seems to be an intrinsic function so I can't check the source.

SetString copies EXACTLY the length you specify, no more, no less.  It is roughly equivalent to:

var S: String;
//SetString(S, Source, Length);
SetLength(S, Length);
Move(Source^, Pointer(S)^, Length * SizeOf(Char));

 

Edited by Remy Lebeau

Share this post


Link to post
4 hours ago, Remy Lebeau said:

SetString copies EXACTLY the length you specify

Okay, so do we agree that it's not equivalent to StrLPas?

Share this post


Link to post
42 minutes ago, Anders Melander said:

Okay, so do we agree that it's not equivalent to StrLPas?

Not equivalent as-is, no.  However, StrPas() converts a null-terminated C-style string to a Delphi string. A StrLPas()-like function would convert a C-style string to a Delphi string up to a given length or the null terminator, whichever is reached first.  You can easily write your own StrLPas() function using SetString(), eg:

function StrLPas(const Str: PChar; const MaxLength: Integer): String;
var
  L: Integer;
  P: PChar;
begin
  P := Str;
  L := 0;
  while (P^ <> #0) and (L < MaxLength) do
  begin
    Inc(P);
    Inc(L);
  end;
  SetString(Result, Str, L);
end;

 

Share this post


Link to post
6 hours ago, Remy Lebeau said:

A StrLPas()-like function would [...]

Yes. Like I implied.

 

6 hours ago, Remy Lebeau said:

You can easily write your own StrLPas() function

Or just use the code I used:

On 6/15/2020 at 10:47 PM, Anders Melander said:

SetLength(PaperName, 64);
StrLCopy(PChar(PaperName), p, 64);
PaperName := PChar(PaperName);

 

In the OP's example it doesn't matter much but a real StrLPas would be more efficient as it would avoid the reallocation (like your example).

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×