Delphi-PRAXiS
Seite 1 von 3  1 23      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Zip erstellen über Shell API (https://www.delphipraxis.net/146116-zip-erstellen-ueber-shell-api.html)

ConstantGardener 13. Jan 2010 19:59


Zip erstellen über Shell API
 
Hallo zusammen,

ich habe im Blog von TMS Software folgende Unit gefunden. Mit Ihr lassen sich die windowseigenen Zip_Funktionen zum komprimieren und dekomprimieren nutzen.
Sehr komfortabel wenn es funktionieren würde. Die Funktionen kompilieren unter D2010 und D2006 ohne Probleme. Er schmeißt mir aber eine Access Violation beim CopyHere in
der Funktion ShellZip.

Kann das mal einer von Euch testen ? Irgendwelche erleuchtenden Infos ?




Code:
unit ShellZip;

interface

 function NumProcessThreads: integer;
 function ShellZip(zipfile, sourcefolder:string; filter: string = ''): boolean;
 function ShellUnzip(zipfile, targetfolder: string; filter: string = ''): boolean;


implementation

uses Comobj, Windows,Tlhelp32;

const
  SHCONTCH_NOPROGRESSBOX = 4;
  SHCONTCH_AUTORENAME = 8;
  SHCONTCH_RESPONDYESTOALL = 16;
  SHCONTF_INCLUDEHIDDEN = 128;
  SHCONTF_FOLDERS = 32;
  SHCONTF_NONFOLDERS = 64;

function ShellUnzip(zipfile, targetfolder: string; filter: string = ''): boolean;
var
  shellobj: variant;
  srcfldr, destfldr: variant;
  shellfldritems: variant;
begin
  shellobj := CreateOleObject('Shell.Application');

  srcfldr := shellobj.NameSpace(zipfile);
  destfldr := shellobj.NameSpace(targetfolder);

  shellfldritems := srcfldr.Items;
  if (filter <> '') then
    shellfldritems.Filter(SHCONTF_INCLUDEHIDDEN or SHCONTF_NONFOLDERS or SHCONTF_FOLDERS,filter);

  destfldr.CopyHere(shellfldritems, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL);
end;

function NumProcessThreads: integer;
var
  hsnapshot: THandle;
  Te32: TTHREADENTRY32;
  proch: dword;
  procthreads: integer;
begin
  procthreads := 0;

  proch := GetCurrentProcessID;

  hSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

  Te32.dwSize := sizeof(TTHREADENTRY32);

  if Thread32First(hSnapShot, Te32) then
  begin
    if te32.th32OwnerProcessID = proch then
      inc(procthreads);

    while Thread32Next(hSnapShot, Te32) do
    begin
      if te32.th32OwnerProcessID = proch then
        inc(procthreads);
    end;
  end;
  CloseHandle (hSnapShot);
  Result := procthreads;
end;

function ShellZip(zipfile, sourcefolder:string; filter: string = ''): boolean;
const
  emptyzip: array[0..23] of byte = (80,75,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var
  ms: TMemoryStream;
  shellobj: variant;
  srcfldr, destfldr: variant;
  shellfldritems: variant;
  numt: integer;
begin
  if not FileExists(zipfile) then
  begin
    // create a new empty ZIP file
    ms := TMemoryStream.Create;
    ms.WriteBuffer(emptyzip, sizeof(emptyzip));
    ms.SaveToFile(zipfile);
    ms.Free;
  end;

  numt := NumProcessThreads;

  shellobj := CreateOleObject('Shell.Application');

  srcfldr := shellobj.NameSpace(sourcefolder);
  destfldr := shellobj.NameSpace(zipfile);

  shellfldritems := srcfldr.Items;

  if (filter <> '') then
    shellfldritems.Filter(SHCONTF_INCLUDEHIDDEN or SHCONTF_NONFOLDERS or SHCONTF_FOLDERS,filter);

  destfldr.CopyHere(shellfldritems, 0);

  // wait till all shell threads are terminated
  while NumProcessThreads <> numt do
  begin
    sleep(100);
  end;
end;

end.

SirThornberry 13. Jan 2010 20:14

Re: Zip erstellen über Shell API
 
Könntest du die genaue Fehlermeldung posten? Also die Accessviolation mit all den angezeigten Adressen und dazugehörigen Operationen.

ConstantGardener 13. Jan 2010 20:33

Re: Zip erstellen über Shell API
 
...aber klar


---------------------------
Tmsshellzipdemo
---------------------------
Zugriffsverletzung bei Adresse 004A4C09 in Modul 'TMSShellZipDemo.exe'. Lesen von Adresse 00000000.
---------------------------
OK
---------------------------

in einem Demoprojekt mit FORM und nur einem Button drauf.

SirThornberry 13. Jan 2010 21:59

Re: Zip erstellen über Shell API
 
Zitat:

Lesen von Adresse 00000000
Das sagt das irgendwo versucht wird von Adresse 0 zu lesen. Und 0 = nil
Es ist also irgendwo etwas nicht initialisiert. Ich vermute das shellfldritems oder dergleichen nil ist.
Am besten du setzt einfach mal einen Haltepunkt und gehst Schritt für Schritt das ganze durch und schaust welchen Wert die Variablen haben. Und noch besser wäre eine Fehlerprüfung rein zu bauen.
Also einfach prüfen ob irgendein Funktionsaufruf fehl schlägt (erkennt man meisten am Rückgabewert) und dann auch nur weitermachen wenn alles ok ist.
Denn derzeit arbeitet die Funktion alles nacheinander ab selbst wenn irgendwo eine Funktion nicht das notwendige für den weiteren Ablauf zurück gibt.

ConstantGardener 13. Jan 2010 22:18

Re: Zip erstellen über Shell API
 
Hallo SirThornberry,

das ist schon klar. Die Funktion ist nur so aus dem Blog kopiert. Ich habe noch keinerlei Veränderungen eingebaut. Da fehlen z.B. auch noch die Rückgabewerte usw. Ich wollte es erstmal im Orginal von TMS (Bruno der Chef) versuchen. Er scheint aber das Shellobject, bzw. Teile davon nicht oder falsch zu initialisieren. Scheinbar kennt er das Objekt und die Propertys aber, da es ja kompiliert.

Hast Du es mal bei Dir versucht ?

paperboy 13. Jan 2010 23:23

Re: Zip erstellen über Shell API
 
hey ConstantGardener,

Erstmal vorweg:
Mich wundert das es sich für dich überhaupt so kompilieren lässt.. da wird doch ein MemoryStream und die Funktion FileExists verwendet?!
Also ich musste noch die Classes und SysUtils Units einbinden damit das Ding überhaupt anstandslos kompiliert wird :gruebel:

Danach hab ich aber keine Probleme beim erstellen von zip Dateien... Naja bis auf die Tatsache das sich mein Testapp aufgrund der Schleife

Delphi-Quellcode:
while NumProcessThreads <> numt do
begin
  sleep(100);
end;
bei einigen Archiven aufhängt...

Beim entpacken in einen Ordner gibt es bei mir nur Probleme wenn der Ordner nicht vorhanden ist...

Wo genau kracht es denn bei dir?

ConstantGardener 13. Jan 2010 23:30

Re: Zip erstellen über Shell API
 
Ja, da hast Du recht. Die SysUtils fehlen hier im Quelltext. Hatte Sie aber natürlich drin. Frage mich gerade wo die geblieben Sind beim Kopieren. :gruebel:

How ever, kompiliert bekomme ich es.

Bei

Delphi-Quellcode:
  destfldr.CopyHere(shellfldritems, 0);
knallts. Das Zip Archiv bleibt lehr. Unzip hab ich noch garnicht probiert.

paperboy 14. Jan 2010 00:04

Re: Zip erstellen über Shell API
 
mhhh...

Wie sieht denn dein Aufruf aus? Benutzt du irgendwelche Filter?
Wohin willst du die zip Datei schreiben? Schreibrechte vorhanden?

Den Fehler bekomm ich namlich auch wenn ich von einem nicht Admin Konto auf C:\ schreiben will...
Denn dann setzt
Delphi-Quellcode:
destfldr := shellobj.NameSpace(zipfile);
destfldr auf $00000000.

sx2008 14. Jan 2010 01:34

Re: Zip erstellen über Shell API
 
Zitat:

Zitat von paperboy
Denn dann setzt
Delphi-Quellcode:
destfldr := shellobj.NameSpace(zipfile);
destfldr auf $00000000.

Genau das ist das Problem.
Die Funktion NameSpace() akzeptiert als Argument nur ein Variant.
WideStrings oder Strings führen zu dem Fehler, dass zwar ein Ergebnis vom Typ varDispatch zurückgeliefert wird, aber der Zeiger = nil ist.
Leichte Schlamperei von Microsoft!

Hier die überarbeitete Unit.
Wichtig ist übrigens, dass die Zipdatei mit absolutem Pfad angegeben wird.
Delphi-Quellcode:
unit ShellZipTool;

interface


type
  TShellZip = class(TObject)
  private
    FFilter: string;
    FZipfile: WideString;
    shellobj: Olevariant;

    procedure CreateEmptyZip;
    function GetNameSpaceObj(x:OleVariant):OleVariant;
    function GetNameSpaceObj_zipfile:OleVariant;

  public
     procedure ZipFolder(const sourcefolder:WideString);
     procedure Unzip(const targetfolder: WideString);

     property Zipfile:WideString read FZipfile write FZipfile;
     property Filter:string read FFilter write FFilter;
  end;

function NumProcessThreads: integer;



implementation

uses Classes, Comobj, Windows, Tlhelp32, SysUtils, Variants;

const
  SHCONTCH_NOPROGRESSBOX = 4;
  SHCONTCH_AUTORENAME = 8;
  SHCONTCH_RESPONDYESTOALL = 16;
  SHCONTF_INCLUDEHIDDEN = 128;
  SHCONTF_FOLDERS = 32;
  SHCONTF_NONFOLDERS = 64;


function IsValidDispatch(const v:OleVariant):Boolean;
begin
  result := (VarType(v)=varDispatch) and Assigned(TVarData(v).VDispatch);
end;


function NumProcessThreads: integer;
var
  hsnapshot: THandle;
  Te32: TTHREADENTRY32;
  proch: dword;
begin
  Result := 0;

  proch := GetCurrentProcessID;

  hSnapShot := CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);

  Te32.dwSize := sizeof(TTHREADENTRY32);

  if Thread32First(hSnapShot, Te32) then
  begin
    if te32.th32OwnerProcessID = proch then
      inc(Result);

    while Thread32Next(hSnapShot, Te32) do
    begin
      if te32.th32OwnerProcessID = proch then
        inc(Result);
    end;
  end;
  CloseHandle(hSnapShot);
end;



{ TShellZip }

procedure TShellZip.CreateEmptyZip;
const
  emptyzip: array[0..23] of byte = (80,75,5,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var
  ms: TMemoryStream;
begin
  // create a new empty ZIP file
  ms := TMemoryStream.Create;
  try
    ms.WriteBuffer(emptyzip, sizeof(emptyzip));
    ms.SaveToFile(Zipfile);
  finally
    ms.Free;
  end;
end;

function TShellZip.GetNameSpaceObj(x:OleVariant): OleVariant;
begin
  // WARNING:
  // the argument of .NameSpace must be a OleVariant
  // don't change "x" to string or WideString
  Result := shellobj.NameSpace(x);
end;

function TShellZip.GetNameSpaceObj_zipfile: OleVariant;
begin
  Result := GetNameSpaceObj(Zipfile);
  if not IsValidDispatch(Result) then
     raise EInvalidOperation.CreateFmt('<%s> invalid zipfile', [zipfile]);
end;


procedure TShellZip.ZipFolder(const sourcefolder: WideString);
var
  srcfldr, destfldr: OleVariant;
  shellfldritems: Olevariant;
  numt: integer;
begin
  if not FileExists(zipfile) then
  begin
    CreateEmptyZip;
  end;

  numt := NumProcessThreads;

  shellobj := CreateOleObject('Shell.Application');

  srcfldr := GetNameSpaceObj(sourcefolder);
  if not IsValidDispatch(srcfldr) then
     raise EInvalidOperation.CreateFmt('<%s> invalid source', [sourcefolder]);

  destfldr := GetNameSpaceObj_zipfile;

  shellfldritems := srcfldr.Items;

  if (filter <> '') then
    shellfldritems.Filter(SHCONTF_INCLUDEHIDDEN or SHCONTF_NONFOLDERS or SHCONTF_FOLDERS,filter);

  destfldr.CopyHere(shellfldritems, 0);

  // wait till all shell threads are terminated
  while NumProcessThreads <> numt do
  begin
    sleep(100);
  end;
end;


procedure TShellZip.Unzip(const targetfolder: WideString);
var
  srcfldr, destfldr: Olevariant;
  shellfldritems: Olevariant;
begin
  shellobj := CreateOleObject('Shell.Application');

  srcfldr := GetNameSpaceObj_zipfile;

  destfldr := GetNameSpaceObj(targetfolder);
  if not IsValidDispatch(destfldr) then
     raise EInvalidOperation.CreateFmt('<%s> invalid target folder', [targetfolder]);

  shellfldritems := srcfldr.Items;
  if (filter <> '') then
    shellfldritems.Filter(SHCONTF_INCLUDEHIDDEN or SHCONTF_NONFOLDERS or SHCONTF_FOLDERS,filter);

  destfldr.CopyHere(shellfldritems, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL);
end;

end.

Garfield 14. Jan 2010 06:07

Re: Zip erstellen über Shell API
 
Interessantes Thema. Kann man da auch einzelne Dateien oder Dateilisten verwenden oder geht das nur mit Verzeichnissen?

Ist es Absicht, dass die Variablen FFilter und FZipfile nur bei der Deklaration der Properties verwendet werden?


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:52 Uhr.
Seite 1 von 3  1 23      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz