Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2016-07

Porting code from Windows D2007, missing Windows functions

14 messages · 5 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I am porting a Delphi2007 utility from Windows to Linux (Raspbian Jessie). It uses a unit I have downloaded from the web, which was said to support FreePascal too.

But now I am getting a number of missing identifier errors as follows Function calls:
- SetFilePointer
- GetFileTime
- FileTimeToLocalFileTime
- FileTimeToDosDateTime
- SetEndOfFile

Constants:
- FILE_CURRENT
- INVALID_HANDLE_VALUE

It seems like these are defined in Windows and now in FPC they are elsewhere.

The uses clause of the unit has the following content:

uses
{$ifdef MSWINDOWS}
  Windows,
{$else}
  LibC, <== Error here, not found
  Types,
{$endif}
  SysUtils;

It could not find LibC and when I asked on the Lazarus list I got a reply to try Unix and/or BaseUnix instead. But this does not work so I guess these functions are in another FPC unit somewhere.

What should I add to the uses clause in place of LibC to get it to compile on Linux?

I am using FPC 3.0 and Lazarus 1.6 on a Raspberry Pi3 with Raspbian Jessie.

Bo Berglund Developer in Sweden

Reply by email

Sven Barth
to Bo Berglund: I am porting a Delphi2007 utility from Windows to Linux (Raspbian Jessie). It uses a unit…
"Bo Berglund" wrote 19 lines
I am porting a Delphi2007 utility from Windows to Linux (Raspbian Jessie). It uses a unit I have downloaded from the web, which was said to support FreePascal too. But now I am getting a number of missing identifier errors as follows Function calls: - SetFilePointer - GetFileTime - FileTimeToLocalFileTime - FileTimeToDosDateTime - SetEndOfFile Constants: - FILE_CURRENT - INVALID_HANDLE_VALUE It seems like these are defined in Windows and now in FPC they are elsewhere.

Those functions and constants don't exist on non-Windows. Try to convert your code to one of the abstractions Free Pascal (and also Delphi) provides, e.g. FileOpen() and friends or TFileStream.

Show quoted text 17 lines
The uses clause of the unit has the following content: uses {$ifdef MSWINDOWS} Windows, {$else} LibC, <== Error here, not found Types, {$endif} SysUtils; It could not find LibC and when I asked on the Lazarus list I got a reply to try Unix and/or BaseUnix instead. But this does not work so I guess these functions are in another FPC unit somewhere. What should I add to the uses clause in place of LibC to get it to compile on Linux?

The LibC unit only exists on i386-linux and only for compatibility for Kylix. Other than that you should consider that unit as deprecated and instead use units like Unix, BaseUnix or Linux, depending on what functionality you need.

Regards,
Sven

Reply by email

Bo Berglund
to Sven Barth
Sven Barth wrote 47 lines
Am 14.07.2016 07:08 schrieb "Bo Berglund" : > > I am porting a Delphi2007 utility from Windows to Linux (Raspbian > Jessie). It uses a unit I have downloaded from the web, which was said > to support FreePascal too. > > But now I am getting a number of missing identifier errors as follows > Function calls: > - SetFilePointer > - GetFileTime > - FileTimeToLocalFileTime > - FileTimeToDosDateTime > - SetEndOfFile > > Constants: > - FILE_CURRENT > - INVALID_HANDLE_VALUE > > It seems like these are defined in Windows and now in FPC they are > elsewhere. Those functions and constants don't exist on non-Windows. Try to convert your code to one of the abstractions Free Pascal (and also Delphi) provides, e.g. FileOpen() and friends or TFileStream. > The uses clause of the unit has the following content: > > uses > {$ifdef MSWINDOWS} > Windows, > {$else} > LibC, <== Error here, not found > Types, > {$endif} > SysUtils; > > It could not find LibC and when I asked on the Lazarus list I got a > reply to try Unix and/or BaseUnix instead. But this does not work so I > guess these functions are in another FPC unit somewhere. > > What should I add to the uses clause in place of LibC to get it to > compile on Linux? The LibC unit only exists on i386-linux and only for compatibility for Kylix. Other than that you should consider that unit as deprecated and instead use units like Unix, BaseUnix or Linux, depending on what functionality you need.

OK,
that was bad news for me because I have included the unit (PasZip from Synopse) in order to make possible for my application to write zipfiles containing measurement result files. I had a Windows specific dll earlier and wanted to switch to multi-platform by using a non-operatingsystem dependent method. On Synopse's website they state that their code is portable across operating systems and specifically mention FPC. So I grabbed PasZip because it encapsulates the zipping in one pas unit and an include file.

I do not see how I can dig down intp PasZip and correct it for multi-platform use given my limited knowledge of the innards of Delphi/FPC.

Anyway thanks for pointing this out so I don't waste time trying to find a unit to use to solve the problem.

I have to contact Synopse, even though they seem not so responsive...

Bo Berglund Developer in Sweden

Reply by email

Michael Van Canneyt
to Bo Berglund
Bo Berglund wrote 19 lines
OK, that was bad news for me because I have included the unit (PasZip from Synopse) in order to make possible for my application to write zipfiles containing measurement result files. I had a Windows specific dll earlier and wanted to switch to multi-platform by using a non-operatingsystem dependent method. On Synopse's website they state that their code is portable across operating systems and specifically mention FPC. So I grabbed PasZip because it encapsulates the zipping in one pas unit and an include file. I do not see how I can dig down intp PasZip and correct it for multi-platform use given my limited knowledge of the innards of Delphi/FPC. Anyway thanks for pointing this out so I don't waste time trying to find a unit to use to solve the problem. I have to contact Synopse, even though they seem not so responsive...

In my experience, Synopse contains some very valuable code, but is very windows & GUI centric.

Michael.

Reply by email

Graeme Geldenhuys
to Bo Berglund: On Thu, 14 Jul 2016 07:55:05 +0200, Sven Barth that was bad news for me because I have…
Bo Berglund wrote
I have to contact Synopse, even though they seem not so responsive...

That seems to be the best option for you at the moment.

If you have the time, it might be worth abstracting out the zip reading functionality so you can swap out the actual compression components used as needed. This will allow your application to change compression tools in future, without your application code needing any changes. This is always a good design philosophy when it comes to using 3rd party components.

While working at a previous employer we use the TurboPower suite of compression components under Windows and Linux with great success, but could also swap it out for the FPC included ZLib unit.

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp

Reply by email

Bo Berglund
to Graeme Geldenhuys
Graeme Geldenhuys wrote 10 lines
If you have the time, it might be worth abstracting out the zip reading functionality so you can swap out the actual compression components used as needed. This will allow your application to change compression tools in future, without your application code needing any changes. This is always a good design philosophy when it comes to using 3rd party components. While working at a previous employer we use the TurboPower suite of compression components under Windows and Linux with great success, but could also swap it out for the FPC included ZLib unit.

Well,
I do have the zip *writing* in one single place as a method for the class that handles the communication. It gets a target zipfile name and a stringlist containing the files to zip as arguments, so that is pretty portable.

Here is how it looks like:

uses PasZip;
function TSSCommHandler.ZipFiles(TargetFile: string; FileList:
TStringList): boolean;
var
  ZW: TZipWrite;
  i: integer;
  FileName: string;
begin
  Result := false;
  ZW := TZipWrite.Create(TargetFile);
  try
    try
      for i := 0 to FileList.Count-1 do
      begin
        FileName := FileList[i];
        if not FileExists(FileName) then Continue;
        ZW.AddDeflated(FileName);
      end;
      Result := true;
    except
      on E: Exception do
      begin
        FLastError := 'Zip error: ' + E.Message;
        exit;
      end;
    end;
  finally
    ZW.Free;
  end;
end;

What I need elsewhere in the code is not depening on PasZip, the call to create the zipfile is simply:

ZipFiles(TargetFile, FileList);

So with the proper set of conditionals I could have different implementations of this method for Windows and Linux.

Do you have an example of how to do the above with FPC ZLib?

Bo Berglund Developer in Sweden

Reply by email

Graeme Geldenhuys
to Bo Berglund
Bo Berglund wrote 10 lines
function TSSCommHandler.ZipFiles(TargetFile: string; FileList: TStringList): boolean; var ZW: TZipWrite; i: integer; FileName: string; begin Result := false; ZW := TZipWrite.Create(TargetFile);

I would replace that with a Factory Method call. For example:

  var
    lCompress: TCompressAbs;
    ...
  begin
    lCompress := gCompressFactory.CreateInstance(cZipWrite);
    lCompress.OutputFile := 'something.zip';
    for i := 0 to FileList.Count-1 do
      lCompress.AddFile(FileList[i]);

So later when you want to switch to say TurboPower or ZLib, you simply need to change one line.

    lCompress := gCompressFactory.CreateInstance(cZLib);
or
    lCompress := gCompressFactory.CreateInstance(cTPAbbrevia);

What I also do in my applications is to reduce that to a single constant, so only one location (or compiler define) needs to change. I do this always for database connections with tiOPF too.

eg:

  -----------[ constants.pas ]-----------
  const
    {$IFDEF ZIPWRITE}
      cCompress = cZipWrite;
    {$ENDIF}
    {$IFDEF ZLIB}
      cCompress = cZLib;
    {$ENDIF}
    {$IFDEF ABBREVIA}
      cCompress = cTPAbbrevia;
    {$ENDIF}

then everywhere in my application I use the new constant

    lCompress := gCompressFactory.CreateInstance(cCompress);

So now a simple single project wide compiler define change and recompile, and my program will use a different compression component suite throughout.

Do you have an example of how to do the above with FPC ZLib?

Yes, and even a nice chapter on explaining it all.

http://tiopf.sourceforge.net/Doc/Concepts/6_TheAdaptorForDatabaseIndependence.shtml

See the following units in the tiOPF’s “tiopf2” branch:

Git repository:
https://sourceforge.net/p/tiopf/code/ci/tiopf2/tree/

Files:
Core/tiCompress.pas
Options/tiCompressNone.pas
Options/tiCompressZLib.pas

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp

Reply by email

Michael Van Canneyt
to Bo Berglund: On Thu, 14 Jul 2016 10:09:36 +0100, Graeme Geldenhuys I do have the zip *writing* in one…
Bo Berglund wrote 63 lines
On Thu, 14 Jul 2016 10:09:36 +0100, Graeme Geldenhuys wrote: > If you have the time, it might be worth abstracting out the zip reading > functionality so you can swap out the actual compression components used > as needed. This will allow your application to change compression tools > in future, without your application code needing any changes. This is > always a good design philosophy when it comes to using 3rd party components. > > While working at a previous employer we use the TurboPower suite of > compression components under Windows and Linux with great success, but > could also swap it out for the FPC included ZLib unit. > Well, I do have the zip *writing* in one single place as a method for the class that handles the communication. It gets a target zipfile name and a stringlist containing the files to zip as arguments, so that is pretty portable. Here is how it looks like: uses PasZip; function TSSCommHandler.ZipFiles(TargetFile: string; FileList: TStringList): boolean; var ZW: TZipWrite; i: integer; FileName: string; begin Result := false; ZW := TZipWrite.Create(TargetFile); try try for i := 0 to FileList.Count-1 do begin FileName := FileList[i]; if not FileExists(FileName) then Continue; ZW.AddDeflated(FileName); end; Result := true; except on E: Exception do begin FLastError := 'Zip error: ' + E.Message; exit; end; end; finally ZW.Free; end; end; What I need elsewhere in the code is not depening on PasZip, the call to create the zipfile is simply: ZipFiles(TargetFile, FileList); So with the proper set of conditionals I could have different implementations of this method for Windows and Linux. Do you have an example of how to do the above with FPC ZLib?

You can find this almost verbatim in the examples of paszlib, using the zipper unit.

Michael.

Reply by email

Bo Berglund
to Michael Van Canneyt
Michael Van Canneyt wrote 5 lines
> Do you have an example of how to do the above with FPC ZLib? You can find this almost verbatim in the examples of paszlib, using the zipper unit.

The one I found is this (a command line utility):

uses
  Zipper;
var
  OurZipper: TZipper;
  I: Integer;
begin
  OurZipper := TZipper.Create;
  try
    OurZipper.FileName := ParamStr(1);
    for I := 2 to ParamCount do
      OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));
    OurZipper.ZipAllFiles;
  finally
    OurZipper.Free;
  end;
end.

It seems straightforward for my method to add all the supplied files in the stringlist the way the example does it.

But this line:
OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I));

seems to add the files to zip into some kind of internal list. Why does it take the same argument twice?

One issue I had with PasZip is that I have not found a way to enter the extract (target) dir for the zipped files.
Can I specify a relative target extract path in the TZipper case, for example relative to the folder where the zipfile being extracted resides?
If so how?

Bo Berglund Developer in Sweden

Reply by email

Michael Van Canneyt
to Bo Berglund
Bo Berglund wrote 37 lines
On Thu, 14 Jul 2016 13:32:38 +0200 (CEST), Michael Van Canneyt wrote: >> Do you have an example of how to do the above with FPC ZLib? > > You can find this almost verbatim in the examples of paszlib, using the > zipper unit. > The one I found is this (a command line utility): uses Zipper; var OurZipper: TZipper; I: Integer; begin OurZipper := TZipper.Create; try OurZipper.FileName := ParamStr(1); for I := 2 to ParamCount do OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I)); OurZipper.ZipAllFiles; finally OurZipper.Free; end; end. It seems straightforward for my method to add all the supplied files in the stringlist the way the example does it. But this line: OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I)); seems to add the files to zip into some kind of internal list. Why does it take the same argument twice?

Because you can add a file with a different filename to the zip. e.g. an absolute filename with a relative filename. Or add a case sensitive filename for the filesystem with an all-lowercase filename.

Show quoted text 7 lines
One issue I had with PasZip is that I have not found a way to enter the extract (target) dir for the zipped files. Can I specify a relative target extract path in the TZipper case, for example relative to the folder where the zipfile being extracted resides? If so how?

With the call above. The first name is the 'real' filename. The second name is the filename that will be stored in the zip.
(or was it vice versa ?)

Michael.

Reply by email

Unknown author
to Bo Berglund: On Thu, 14 Jul 2016 13:32:38 +0200 (CEST), Michael Van Canneyt The one I found is this (a…

On 07/14/2016 08:31 AM, Bo Berglund wrote:
[...]

Show quoted text 5 lines
But this line: OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I)); seems to add the files to zip into some kind of internal list. Why does it take the same argument twice?

i found the following in the interface section of zipper.pp

   TZipFileEntries = Class(TCollection)
   private
     function GetZ(AIndex : Integer): TZipFileEntry;
     procedure SetZ(AIndex : Integer; const AValue: TZipFileEntry);
   Public
     Function AddFileEntry(Const ADiskFileName : String): TZipFileEntry;
     Function AddFileEntry(Const ADiskFileName, AArchiveFileName : String):
TZipFileEntry;
     Function AddFileEntry(Const AStream : TSTream; Const AArchiveFileName :
String): TZipFileEntry;
     Procedure AddFileEntries(Const List : TStrings);
     Property Entries[AIndex : Integer] : TZipFileEntry Read GetZ Write SetZ;
default;
   end;

looks to me like you can zip a disk file or a stream... inside the archive, it appears that each file can have a name... makes sense to use the disk file name as the internal file name... also gives a way for a stream to have a name once archived into the zip...

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list* unless private contact is specifically requested and granted.

Reply by email

Bo Berglund
to Unknown author
Show quoted text 25 lines
On 07/14/2016 08:31 AM, Bo Berglund wrote: [...] > But this line: > OurZipper.Entries.AddFileEntry(ParamStr(I), ParamStr(I)); > > seems to add the files to zip into some kind of internal list. > Why does it take the same argument twice? i found the following in the interface section of zipper.pp TZipFileEntries = Class(TCollection) private function GetZ(AIndex : Integer): TZipFileEntry; procedure SetZ(AIndex : Integer; const AValue: TZipFileEntry); Public Function AddFileEntry(Const ADiskFileName : String): TZipFileEntry; Function AddFileEntry(Const ADiskFileName, AArchiveFileName : String): TZipFileEntry; Function AddFileEntry(Const AStream : TSTream; Const AArchiveFileName : String): TZipFileEntry; Procedure AddFileEntries(Const List : TStrings); Property Entries[AIndex : Integer] : TZipFileEntry Read GetZ Write SetZ; default; end;

Sounds like this is going to work given that the AArchiveFileName can be used as argument (untested):

 ExtractDir := ExtractFileName(TargetZipFile);
 ExtractDir := IncludePathDelimiter(ReplaceFileExt(ExtractDir, ''));
 for i := 0 to FileList.Count-1 do
 begin
   FileName := FileList[i];
   if not FileExists(FileName) then Continue;
   {$ifdef FPC}
   FileNameArc := ExtractDir + ExtractFileName(FileName);
   ZW.Entries.AddFileEntry(FileName, FilenameArc);
   {$else}
   ZW.AddDeflated(FileName);
   {$endif}
 end;

I have not gotten to the point where there are files to zip yet, but the utility builds fine and runs as expected now! And PasZip will be ifdefed out on FPC, only used with Delphi...

Thanks for all the help and suggestions!

Bo Berglund Developer in Sweden

Reply by email