Using 7-Zip from Delphi? - delphi

I would like to use the 7-Zip DLLs from Delphi but have not been able to find decent documentation or examples. Does anyone know how to use the 7-Zip DLLs from Delphi?

As of release 1.102 the JEDI Code Library has support for 7-Zip built into the JclCompression unit. Haven't used it myself yet, though.

Expanding on Oliver Giesen's answer, as with a lot of the JEDI Code Library, I couldn't find any decent documentation, but this works for me:
uses
JclCompression;
procedure TfrmSevenZipTest.Button1Click(Sender: TObject);
const
FILENAME = 'F:\temp\test.zip';
var
archiveclass: TJclDecompressArchiveClass;
archive: TJclDecompressArchive;
item: TJclCompressionItem;
s: String;
i: Integer;
begin
archiveclass := GetArchiveFormats.FindDecompressFormat(FILENAME);
if not Assigned(archiveclass) then
raise Exception.Create('Could not determine the Format of ' + FILENAME);
archive := archiveclass.Create(FILENAME);
try
if not (archive is TJclSevenZipDecompressArchive) then
raise Exception.Create('This format is not handled by 7z.dll');
archive.ListFiles;
s := Format('test.zip Item Count: %d'#13#10#13#10, [archive.ItemCount]);
for i := 0 to archive.ItemCount - 1 do
begin
item := archive.Items[i];
case item.Kind of
ikFile:
s := s + IntToStr(i+1) + ': ' + item.PackedName + #13#10;
ikDirectory:
s := s + IntToStr(i+1) + ': ' + item.PackedName + '\'#13#10;//'
end;
end;
if archive.ItemCount > 0 then
begin
// archive.Items[0].Selected := true;
// archive.ExtractSelected('F:\temp\test');
archive.ExtractAll('F:\temp\test');
end;
ShowMessage(s);
finally
archive.Free;
end;
end;

7 Zip Plugin API
http://www.progdigy.com/?page_id=13

Zip and 7z with NO DLL, try out Synopse:
http://synopse.info/forum/viewtopic.php?pid=163

Delphi now has native, cross platform zip support with TZipFile in XE2:
How to extract zip files with TZipFile in Delphi XE2 and FireMonkey

If you intend to use 7Zip only for zip and unzip take a look at the TZip component.
I have written a small wrapper for my own purposes, which you can find in the Zipper.pas file, feel free to reuse.

I tried many solutions and had problems, this one worked.
Download https://github.com/zedalaye/d7zip
Copy 7z.dll and sevenzip.pas to your project diroctory and add sevenzip.pas to your project.
Then you can use this to unzip:
using sevenzip;
procedure Unzip7zFile (zipFullFname:string);
var
outDir:string;
begin
with CreateInArchive(CLSID_CFormat7z) do
begin
OpenFile(zipFullFname);
outDir := ChangeFileExt(zipFullFname, '');
ForceDirectories (outDir);
ExtractTo(outDir);
end;
end;
Usage:
Unzip7zFile(ExtractFilePath(Application.ExeName) + 'STR_SI_FULL_1000420.7z');

Related

Delphi VCL TMediaPlayer: file path/name length limit

Using Delphi 10.4 Community Edition, VCL, Windows 10 64bit, although the compiled .exe application is 32bit.
The VCL's TMediaPlayer seems to have a file path/name length limit of 128 characters. Is this really an internal limitation? Is there any way to access longer file paths/names?
I was coding a small soundPad player by using the TMediaPlayer component.
The installer I am using installs the .exe program in the user's home directory, and at the same time a few sample audio files in the program's root directory.
In this case, the path to the audio file may be quite long. For example:
C:\Users\user\AppData\Local\Programs\MySoundPlayer\ThisIsMySoundWithAVeryLongFileNameThereIsSomeCopyrightInfoAndSomeOther.wav
When trying to play such a file, TMediaPlayer will give an error message:
Exception class name = 'EMCIDeviceError'
Exception message = 'Invalid filename. Make sure the filename has 8 characters, a period, and an extension.'
I tried different lengths in the file name, and it looks like 127 is the maximum length.
So, the VCL TMediaPlayer component does not recognize file paths / names longer than 127 characters?
I tried the same code with a Delphi FMX app, and FMX's TMediaPlayer worked ok. It seems that the maximum file path and name length of the FMX TMediaPlayer is 259, which is quite sufficient.
The length 259 seem to be the limit of the File Explorer overall...
It is said that the VCL's TMediaPlayer component is starting to become obsolete, and is only involved in backward compatibility reasons. But what can replace it in the future?
So, I guess I have to move on to FMX and learn its secrets. Is VCL a receding component system?
procedure TForm1.PlayButtonClick(Sender: TObject);
var
pathstring, playerfilename, playstring : string;
begin
try
pathstring := ExtractFilePath(Application.ExeName);
playerfilename := 'ThisIsMySoundWithAVeryLongFileNameThereIsSomeCopyrightInfoAndSomeOther.wav';
playstring := pathstring + playerfilename;
MediaPlayer1.FileName := playstring;
MediaPlayer1.Open;
MediaPlayer1.Play;
except
on E : Exception do
begin
ShowMessage('Exception class name = ' + E.ClassName);
ShowMessage('Exception message = ' + E.Message);
end;
end;
end;
Per this answer to mciSendString() won't play an audio file if path is too long:
Here, mmioOpen is called with MMIO_PARSE flag to convert file path to fully qualified file path. According to MSDN, this has a limitation:
The buffer must be large enough to hold at least 128 characters.
That is, buffer is always assumed to be 128 bytes long. For long filenames, the buffer turns out to be insufficient and mmioOpen returns error, causing mciSendCommand to think that sound file is missing and return MCIERR_FILENAME_REQUIRED.
The Invalid filename error message you are seeing is the system text for the MCIERR_FILENAME_REQUIRED error code.
The VCL's TMediaPlayer is based on MCI and internally uses mciSendCommand(), which is just the binary version of mciSendString(). They both suffer from the same problem.
The preferred fix is to either use shorter paths, or use a more modern audio API.
However, since mmioInstallIOProc() can be used to let TMediaPlayer play media files from memory instead of files, I think a similar solution could be used to play files with long file paths, since you could take over the responsibility of opening/reading/seeking a file, bypassing the path limitation of the troublesome mmioOpen(). Just replace the TResourceStream in that code with a TFileStream, and update the MMIOM_READ and MMIOM_SEEK handlers accordingly to read/seek that TFileStream.
For example (untested, might need some tweaking):
uses
Winapi.MMSystem;
var
ccRES: FOURCC;
playstring: string;
function MAKEFOURCC(ch0, ch1, ch2, ch3: BYTE): FOURCC;
begin
Result := DWORD(ch0) or (DWORD(ch1) shl 8) or (DWORD(ch2) shl 16) or (DWORD(ch3) shl 24);
end;
function MyLongFileIOProc(lpMMIOInfo: PMMIOInfo; uMessage: UINT; lParam1, lParam2: LPARAM): LRESULT; stdcall;
var
FStrm: TFileStream;
NumRead: Integer;
function GetFileStream: TFileStream;
begin
Move(lpMMIOInfo.adwInfo, Result, SizeOf(TFileStream));
end;
procedure SetFileStream(Stream: TFileStream);
begin
Move(Stream, lpMMIOInfo.adwInfo, SizeOf(TFileStream));
end;
begin
if uMessage = MMIOM_OPEN then
begin
try
FStrm := TFileStream.Create(playstring, fmOpenRead or fmShareDenyWrite);
except
SetFileStream(nil);
Exit(MMIOM_CANNOTOPEN);
end;
SetFileStream(FStrm);
lpMMIOInfo.lDiskOffset := 0;
end else
begin
FStrm := GetFileStream;
case uMessage of
MMIOM_CLOSE: begin
SetFileStream(nil);
FStrm.Free;
end;
MMIOM_READ: begin
NumRead := FStrm.Read(Pointer(lParam1)^, lParam2);
Inc(lpMMIOInfo.lDiskOffset, NumRead);
Exit(NumRead);
end;
MMIOM_SEEK: begin
FStrm.Seek(Int64(lParam1), TSeekOrigin(lParam2));
lpMMIOInfo.lDiskOffset := FStrm.Position;
Exit(lpMMIOInfo.lDiskOffset);
end;
end;
Exit(MMSYSERR_NOERROR);
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ccRES := MAKEFOURCC(Ord('L'), Ord('F'), Ord('N'), Ord(' '));
mmioInstallIOProc(ccRES, TFNMMIOProc(MyLongFileIOProc), MMIO_INSTALLPROC);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
mmioInstallIOProc(ccRES, nil, MMIO_REMOVEPROC);
end;
procedure TForm1.PlayButtonClick(Sender: TObject);
var
pathstring, playerfilename : string;
begin
try
pathstring := ExtractFilePath(Application.ExeName);
playerfilename := 'ThisIsMySoundWithAVeryLongFileNameThereIsSomeCopyrightInfoAndSomeOther.wav';
playstring := pathstring + playerfilename;
MediaPlayer1.DeviceType := dtWaveAudio;
MediaPlayer1.FileName := 'playstring.LFN+';
MediaPlayer1.Open;
MediaPlayer1.Play;
except
on E : Exception do
begin
ShowMessage('Exception class name = ' + E.ClassName);
ShowMessage('Exception message = ' + E.Message);
end;
end;
end;

Save a TiniFile from application to iOS

I am very new to the iOS platform. I am trying to save an INI file for my application. The problem is that I can't get a path with write permission.
Here is my code:
ini := TIniFile.Create(GetHomePath + '/user.dat');
try
ini.WriteString('data','user',edtuser.Text);
ini.WriteString('data','descr',edt1.Text);
finally
ini.Free;
end;
I get an exception that the file can't be created. How can I get a writable path using Firemonkey?
Use TPath.GetDocumentsPath (and use TPath.Combine instead of concatenation, to remove the hard-coded /):
uses
System.IOUtils;
ini := TIniFile.Create(TPath.Combine(TPath.GetDocumentsPath, 'user.dat'));
Using TPath.GetDocumentsPath works across all supported platforms (Win32, Win64, OSX, iOS, and Android) transparently, and using TPath.Combine will automatically add the TPath.DirectorySeparatorChar, so you don't have to manually concatenate them.
If you prefer to do it yourself, though:
var
IniName: string;
begin
IniName := TPath.GetDocumentsPath + TPath.DirectorySeparatorChar + 'user.dat';
Ini := TIniFile.Create(IniName);
try
// Rest of code
finally
Ini.Free;
end;
end;
May be this or this can help you
uses INIFiles;
function TForm6.MyINIFilePath: string;
begin
// Result := GetHomePath + PathDelim + 'Library' + PathDelim+'My.ini';
Result := GetHomePath + PathDelim + 'Documents' + PathDelim+'MyD.ini';
end;

How am I supposed to use JEDI's JCLCompression to create a 7z archive?

I am trying to create a 7z archive of certain files using Delphi 2009.
The code below seems to work, but all of the items in the resulting 7z file are of zero size. All of the file names that are in the archive are correct, but they shouldn't be zero size.
How can I add files properly to a 7z archive using JCLCompression?
var
fname, archiveFileName: string;
arch: TJclUpdateArchive;
archiveclass: TJCLUpdateArchiveClass;
sr: TSearchRec;
begin
fname := GetDesktop + 'Support.7z';
archiveclass := GetArchiveFormats.FindUpdateFormat(fname);
if not Assigned(archiveclass) then
raise Exception.Create('Could not determine the Format of ' + fname);
arch := archiveclass.Create(fname);
try
with arch do
begin
if FindFirst(uFolder + '*.*', faAnyFile, sr) = 0 then
begin
repeat
AddFile(ExtractFileName(sr.Name), sr.Name);
until FindNext(sr) <> 0;
FindClose(sr);
end;
Compress;
end;
finally
arch.free;
end;
end;
Never, ever, having jused JCLCompression my answer may wel be totally wrong, but don't you have to specify the folder somewhere ? You are only adding the filenames.

TOpenDialog And Spaces

I have a project on Lazarus that I want to compile a source using gcc, for this I have a TOpenDialog called OpenDialog1 and a TProcess called AProcess.
I call gcc with this code:
procedure TFormMain.btCompileClick(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
AProcess := TProcess.Create(nil);
try
AProcess.CommandLine := 'gcc.exe ' + OpenDialog1.FileName;
AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
AProcess.Execute;
OutputMemo.Lines.BeginUpdate;
OutputMemo.Lines.Clear;
OutputMemo.Lines.LoadFromStream(AProcess.Output);
OutputMemo.Lines.EndUpdate;
finally
AProcess.Free;
end;
end;
end;
It compiles ok(the project on Lazzarus), but when I was testing it, by trying to compile the source test.c, that is located at C:\Documents and Settings\Nathan Campos\Desktop, I got this on OutputMemo:
'C:\Documents': No such file or directory
Then, OpenDialog1 isn't getting the full path with the spaces, or gcc can't locate it on the folder with spaces.
Any suggestion to help me on this?
It needs to be in quotes so spaces won't trip it up. So like this:
AProcess.CommandLine := 'gcc.exe "' + OpenDialog1.FileName + '"';
That should work.

Image list loading fails on Delphi 6 and Vista service pack 2

Delphi 6 on Vista service pack 2 seems that can't load imagelist from dfm and save back again in the IDE.
The project with the dfm corrupted can't be rebuilt.
the error when I run the project is:
EReadError Error reading imagelist1.Bitmap: Failed to read ImageList
data from stream
any suggestion?
thanks in advance
Have you done anything funny to your Delphi installation, such as adding a delphi32.exe.manifest file to Delphi's directory in an attempt to make the IDE have XP or Vista theming? Don't do that. If you have that file there, delete it, and you should be back to normal.
The image-list format changed with version 6 of the Common Controls library, and Delphi 6 is not capable of using it. The manifest tells the IDE to use version 6, so when it saves your DFM, it uses that format. Then, when loading, prior versions can't read it anymore.
The problem may be on ImageList_Write of the comctl32.dll
// delphi 6
procedure TCustomImageList.WriteData(Stream: TStream);
var
SA: TStreamAdapter;
begin
SA := TStreamAdapter.Create(Stream);
try
if not ImageList_Write(Handle, SA) then
raise EWriteError.CreateRes(#SImageWriteFail);
finally
SA.Free;
end;
end;
// delphi 2005
procedure TCustomImageList.WriteData(Stream: TStream);
var
SA: TStreamAdapter;
ComCtrlHandle: THandle;
const
ILP_DOWNLEVEL = 1;
begin
if CachedComCtrlVer = 0 then
begin
CachedComCtrlVer := GetFileVersion(comctl32);
if CachedComCtrlVer >= ComCtlVersionIE6 then
begin
ComCtrlHandle := GetModuleHandle(comctl32);
if ComCtrlHandle <> 0 then
ImageListWriteExProc := GetProcAddress(ComCtrlHandle, 'ImageList_WriteEx'); { Do not localize }
end;
end;
SA := TStreamAdapter.Create(Stream);
try
{ See if we should use the new API for writing image lists in the old
format. }
if Assigned(ImageListWriteExProc) then
begin
if ImageListWriteExProc(Handle, ILP_DOWNLEVEL, SA) <> S_OK then
raise EWriteError.CreateRes(#SImageWriteFail)
end
else if not ImageList_Write(Handle, SA) then
raise EWriteError.CreateRes(#SImageWriteFail);
finally
SA.Free;
end;
end;

Resources