RTollison 0 Posted June 15, 2020 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
Attila Kovacs 631 Posted June 15, 2020 (edited) use DeviceCapabilities() instead of DeviceCapabilitiesA(), then you will also find examples Edited June 15, 2020 by Attila Kovacs Share this post Link to post
Vandrovnik 215 Posted June 15, 2020 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
Anders Melander 1815 Posted June 15, 2020 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; 1 Share this post Link to post
Remy Lebeau 1436 Posted June 15, 2020 (edited) 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 June 15, 2020 by Remy Lebeau 1 Share this post Link to post
Attila Kovacs 631 Posted June 15, 2020 @Anders Melander Wouldn't be a SetString() nicer? Share this post Link to post
Anders Melander 1815 Posted June 15, 2020 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
Anders Melander 1815 Posted June 15, 2020 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. 1 Share this post Link to post
Attila Kovacs 631 Posted June 15, 2020 (edited) it also copies all the 64 chars so the truncate trick is still needed SetString(PaperName, p, 64); PaperName := PChar(PaperName); Edited June 15, 2020 by Attila Kovacs Share this post Link to post
Remy Lebeau 1436 Posted June 16, 2020 (edited) 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 June 16, 2020 by Remy Lebeau Share this post Link to post
Anders Melander 1815 Posted June 17, 2020 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
Remy Lebeau 1436 Posted June 17, 2020 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
Anders Melander 1815 Posted June 17, 2020 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