Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Excel-Datei in Dienst + Thread erzeugen und speichern (https://www.delphipraxis.net/181438-excel-datei-dienst-thread-erzeugen-und-speichern.html)

FBrust 14. Aug 2014 14:42

Delphi-Version: 2010

Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

ich möchte in einem Thread, der in einem Dienst läuft, eine Excel-Tabelle erzeugen, mit Daten füllen und unter einem gegebenen Namen speichern.

Ich habe bereits eine Methode, die in einem "normalen" Desktop-Programm erfolgreich eingesetzt wird, für den Thread übernommen.

Allerdings erhalte ich nun die Fehlermeldung "Die Close-Methode des Workbook-Objektes konnte nicht ausgeführt werden" und habe keine Ahnung, wieso.

Hier der Code:

Delphi-Quellcode:
function TResultT.CreateExcelFile(strProgram: string; strPDFName: string; intCustID: integer): string;
var
  strFilename: string;
  Excel     : variant;
  qryTemp   : TIBCQuery;
  intRow    : integer;
  iCnt      : Integer;
  _lcid     : integer;
begin
  Result := '';
  strFileName := StringReplace(strPDFName, '.pdf', '.xls', []);

  try
    try
      OleInitialize(nil);
      Excel := CreateOleObject('Excel.Application');
    except
      ShowMessage('Excel konnte nicht gestartet werden!');
      Result := '';
      Exit;
    end;

    _lcid := GetUserDefaultLCID;
    Excel.Workbooks.Add;
    Excel.Visible := False;            // TRUE = Excel sichtbar.
    Excel.Worksheets[1].Activate;
    Excel.Cells[1,1] := 'blabla';


    // Hier werden dann Daten aus einer Abfrage in die Excel-Tabelle geschrieben

    if (FileExists(strFilename)) then
      begin
        SysUtils.DeleteFile(strFilename);
      end;

    Excel.Worksheets[1].Range['a1', 'a1'].EntireColumn.autofit;
    Excel.Worksheets[1].Range['b1', 'b1'].EntireColumn.autofit;
    Excel.Worksheets[1].Range['c1', 'c1'].EntireColumn.autofit;
    Excel.Worksheets[1].Range['d1', 'd1'].EntireColumn.autofit;
    Excel.Worksheets[1].Range['e1', 'e1'].EntireColumn.autofit;
    Excel.Worksheets[1].Range['f1', 'f1'].EntireColumn.autofit;

    Excel.DisplayAlerts := False;
    try
      Excel.ActiveWorkBook.Close(True, strFileName); // <-- Hier knallts mit der o. a. Fehlermeldung
      Excel.DisplayAlerts := True;
    except
      on E:Exception do
        begin
          WriteLog('Excel-Tabelle "' + strFileName +  '"konnte nicht erzeugt werden, Meldung: ' + E.Message, 1);
          strFileName := '';
        end;
    end;
  finally
    Excel.Quit;
    Excel := unassigned;
    OleUnInitialize;
  end;
  Result := strFilename;
end;
Wie gesagt, ich weiss nicht, wieso jetzt .Close nicht mehr funktioniert. Ich habe auch .SaveAs(strFileName) ausprobiert, mit dem gleichen Ergebnis.

In dem Ordner, in dem die XLS-Datei erzeugt werden soll, werden vorher im gleichen Thread 1 - n PDF-Dateien erzeugt. Das klappt einwandfrei, also vermute ich mal, dass es kein Rechteproblem ist. Excel ist auch installiert.

Hat vielleicht jemand eine Tipp?

Grüße
Frank

Bernhard Geyer 14. Aug 2014 14:44

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Ich hatte schon auch mal mit anderen COM-Schnittstellen die Erfahrung gemacht das man diese nur aus dem Hauptthread der Anwendung bedienen sollte. Ob hier Delphi oder Windows problem bereitet habe ich nicht weiter untersucht.

p80286 14. Aug 2014 15:21

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Zitat:

Zitat von FBrust (Beitrag 1268673)
ich möchte in einem Thread, der in einem Dienst läuft, eine Excel-Tabelle erzeugen, mit Daten füllen und unter einem gegebenen Namen speichern.

Und wohin willst Du schreiben?
'c:\'
'c:\Program files'
'c:\diesesVerzeichnisgibtesnicht'

sind grundsätzlich nicht sooo dolle.

Hat Dein Dienst auch die notwendigen Rechte um eine Datei dort zu schreiben wo Du es kannst?

Gruß
K-H

FBrust 14. Aug 2014 16:25

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

danke für eure Antworten.

der Ordner wird in dem Thread erst erzeugt (funktioniert) und die vorher ebenfalls in diesem Thread erzeugten PDF-Dateien werden auch in diesem Ordner gespeichert. Der Dienst läuft auch als SYSTEM, also dürfte er die Rechte haben.

Braucht der Dienst Extra-Rechte, wenn über OLE eine Datei erzeugt werden soll?

Grüße
Frank

p80286 14. Aug 2014 17:28

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Zitat:

Zitat von FBrust (Beitrag 1268689)
der Ordner wird in dem Thread erst erzeugt (funktioniert) und die vorher ebenfalls in diesem Thread erzeugten PDF-Dateien werden auch in diesem Ordner gespeichert.

wenn das funktioniert, dann sollte eigentlich auch Excel funktionieren.
Ich hab das "SaveAs" von E2013 mal mitgeschnitten, u.U. mußt Du die entsprechenden Parameter mit übergeben:
Code:
ActiveWorkbook.SaveAs Filename:="C:\Temp\willi.xls", FileFormat:=xlExcel8, _
        Password:="", WriteResPassword:="", ReadOnlyRecommended:=False, _
        CreateBackup:=False
Gruß
K-H

himitsu 14. Aug 2014 19:33

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Es könnte natürlich auch sein, daß dieses Excel in einem Dienst einer Session ohne Desktop/UI richtig fnktioniert?

Und sowas wie ShowMessage oder DisplayAlerts hat in einem Dienst sowieso nichts zu suchen. (außer vielleicht mal in einem "Interactive Service")


PS: "StringReplace" -> Delphi-Referenz durchsuchenChangeFileExt

FBrust 15. Aug 2014 12:31

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

vielen Dank für die Tips.

Ich habe jetzt die o. a. Methode in den Hauptthread verlagert, erhalte aber die gleiche Fehlermeldung. Auch die Angabe der zusätzlichen Parameter brachte keinen Erfolg. Vermutlich stimmt Himitsus Annahme, dass diese Vorgehensweise auf diese Art und Weise nicht funktioniert.

Evtl. muss ich das Erzeugen der Excel-Datei in ein VCL-Programm auslagern, dass dann vom Dienst aufgerufen wird, die Excel-Tabelle erzeugt und sich dann wieder beendet.

@Himitsu: Die Methode kannte ich noch garnicht, vielen Dank für den Tip. Und das "Showmessage" ist auch ersetzt.


Grüße
Frank

Jumpy 15. Aug 2014 13:29

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Je nachdem um welches Excel-Dateiformat (alt/neu) es geht und was in der Datei alles gemacht werden soll (nur Daten reinschaufeln oder auch Formatierungen vornehmen) gibt es (auch hier in der DP irgendwo) Projekte, um Excel-Dateien ohne Excel zu erzeugen.

Hier noch ein paar links bzgl. der ursprünglichen Problems:

http://bharathkumaran.wordpress.com/...ndows-service/

http://social.technet.microsoft.com/...updeploylegacy

FBrust 16. Aug 2014 15:41

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

ich habe jetzt (auch um zu vermeiden, dass Excel als Systemvoraussetzung deklariert werden muss, aus http://www.delphipraxis.net/180747-e...ne-office.html die DLL und die Unit verwendet, um eine Testfunktion zu erstellen:

Delphi-Quellcode:
function TResultT.TestExcelfile(intCustID: integer; strResultFile: string): string;
var
  strFile: string;
  strPath: string;
  strFileName: string;
begin
 
  strFileName := 'D:\Ausgabe\test.xls');

  XLSCreate(PAnsiChar(strFileName),xlsfmcreate);
  XLSCreator(PAnsiChar('Test99'));
  XLSFontName(PAnsiChar('Segoe UI'));
  XLSWorkSheetAdd(PAnsiChar('Stichprobenergebnis'));

   // Kopfdaten
  XLSWriteStr(0,1,1, PAnsiChar('Test'));
  XLSWriteStr(0,1,3, PAnsiChar('Kunde'));

  XLSWriteStr(0,2,3, PAnsiChar(GetCustNameByID(intCustID, conData)));

  XLSWriteStr(0,1,4, PAnsiChar('Stichprobe vom:'));

  XLSWriteStr(0,1,5, PAnsiChar('Prüfer/in:'));

  XLSWriteStr(0,1,6, PAnsiChar('Vereinbartes Qualitätsniveau:'));

  XLSDestroy;

end;
Diese funktioniert in einem VCL-Testprogramm einwandfrei, aber nicht im Thread des Dienstes. Es kommt kein Fehler, auch nicht beim Durchsteppen im Debugger, es wird einfach keine Datei erzeugt. Es ist zum Haareraufen.

Eben fällt mir auf, dass FastReport keine Probleme hat, im gleichen Thread einen Bericht als PDF zu exportieren, möglicherweise funktioniert der Excel-Export von FR dann genauso. Ich werde es ausprobieren müssen, denn ansonsten müsste ich eine Lösung entwickeln, die außerhalb des Dienstes läuft :(.

Gruß
Frank

himitsu 16. Aug 2014 20:30

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Schön, daß man nicht weiß, was in der DLL passiert. :stupid:
Es war aber eher sowas wie TXLSFile gemeint, was man eigentlic in der DP finden müsste, aber welches ich grade nicht entdecke.

Ich hatte den Code aber mal versucht in einer Komponente zusammenzufassen.
Siehe anhang. (falls es Probleme gibt, dann eventuell mal in die Version in der enthaltenen 7z gucken ... hab jetzt keine Zeit zu vergleichen, ob sich da, in dem Tag nach erstellung der 7Zip, noch etwas verändert hatte)

[edit]
www.delphipraxis.net/164833-excel.html :oops: (hatte den Thread sogar in der Unit genannt)


Pssst, der Link in deinem Link ist etwas kaputt :zwinker:

marky522 18. Aug 2014 20:36

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

in der DLL wird eigentlich nichts Besonderes gemacht.
Es sind folgende Units eingebunden - betrifft die Version V2, die ja verwendet wurde:

uses
SysUtils,math,Windows,
Classes;

Ich kann mir jedenfalls nicht erklären, warum es nicht geht, bzw. nicht gehen soll.

Kann es sein, dass Du aus einem Dienst heraus die DLL nicht aufrufen kannst oder darfst,
z.B. aufgrund des Verzeichnisses, in dem die DLL liegt?

Gruß Markus

FBrust 19. Aug 2014 13:43

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

die DLL liegt im gleichen Verzeichnis, in dem auch die exe des Dienstes liegt (D:\Ausgabe, also nicht das Programmverzeichnis).

Ich habe wiederum ein Testprogramm erstellt, das auch dort liegt und die gleiche Funktion zum Erstellen der Excel-Datei verwendet - das funktioniert, aber aus dem Dienst heraus passiert nichts, keine Datei, keine Fehlermeldung.


Gruß
Frank

himitsu 19. Aug 2014 14:19

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hast du mal im VirtualStore nachgesehn, ob sie nicht vielleicht dort liegt?

Bzw. in Taskmanager/Prozesse kann man sich auch so Spalten einblenden, ala UAC-Virtualisierung und Datenausführungsverhinderung.
Letzteres sollte hier aber eigentlich egal sein und UAC steht vermutlich auf Deaktiviert, aber das sollte so auch OK sein.


Die DLL wird im Hauptthread geladen und dann im Thread verwendet, oder nur im Thread ge- und entladen?
Zitat:

Zitat von FBrust (Beitrag 1269077)
passiert nichts, ..., keine Fehlermeldung.

Sicher?
http://www.delphipraxis.net/181427-f...ml#post1268581
http://www.delphipraxis.net/180673-s...ml#post1261638
http://www.delphipraxis.net/179601-d...ml#post1252483

FBrust 19. Aug 2014 14:33

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo,

ich hab mich an dem Beispielprogramm orientiert, dort wird die DLL auch nicht explizit ge- und entladen, ich vermute, dass dies in der Unit passiert, aber da kann Markus sicher mehr dazu sagen.

Ansonsten wird die Unit UXLSV2 nur in diesem einen Thread (der ja nur ein Unterthread ist) verwendet.

Hab gerade nachgesehen, im Virtualstore ist die Datei auch nicht.


Gruß
Frank

himitsu 19. Aug 2014 14:51

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Zitat:

Zitat von FBrust (Beitrag 1269090)
ich vermute, dass dies in der Unit passiert,

:nachseh:

"external"
Jupp, wird von der Unit, genauer von Windows (dem Loader) beim Programmstart geladen, also im Haupthtread.

marky522 24. Aug 2014 07:16

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Frank,

ich kann mir nicht erklären, warum die Exceldateierstellung in einem Dienst nicht laufen sollte.

Ich habe Dir mal die für Delphi 2010 Prof. kompilierte Usbiff8.dcu der Usbiff8.pas angehängt zusammen mit einem
Demo-Projekt - also keine DLL, nur Units. In einen Thread habe ich das schon laufen lassen, und es geht.

Würde mich interessieren, ob es damit in einem Dienst klappt.

MfG
Markus

FBrust 24. Aug 2014 17:24

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo Markus,

danke für das Testprogramm.

Ich habe im Dienst nun mit Hilfe des Testprogramms folgende Funktion erstellt:

Delphi-Quellcode:
function TResultT.TestExcelfile(intCustID: integer; strResultFile: string): boolean;
var
  strFile   : string;
  strPath   : string;
  strFileName: string;
  XLSFile   : TXLS;
begin
  Result := True;

  strFileName := 'D:\Ausgabe\test.xls';
  try
    try
      XLSFile          := CreateXLS(PAnsiChar(strFileName),xlsfmcreate);
      XLSFile.Creator  := PAnsiChar('Test99');
      XLSFile.Font.Name := (PAnsiChar('Segoe UI'));
      XLSFile.WorkSheetAdd(PAnsiChar('Stichprobenergebnis'));

       // Kopfdaten
      XLSFile.Write(0,1,1, PAnsiChar('Test'));
      XLSFile.Write(0,1,3, PAnsiChar('Kunde'));

      XLSFile.Write(0,2,3, PAnsiChar(GetCustNameByID(intCustID, conData)));

      XLSFile.Write(0,1,4, PAnsiChar('Stichprobe vom:'));

      XLSFile.Write(0,1,5, PAnsiChar('Prüfer/in:'));

      XLSFile.Write(0,1,6, PAnsiChar('Vereinbartes Qualitätsniveau:'));

      XLSFile.WriteFile;
    except
      on E:Exception do
        begin
          WriteLog('Fehler bei Erzeugen Excel-Tabelle, Meldung: ' + E.Message, 1);
          Result := False;
        end;
    end;
  finally
    XLSFile.Free;
  end;
Beim Durchsteppen im Debugger zeigt sich, dass diese Methode auch ohne Probleme durchlaufen wird. Es wird aber keine Datei geschrieben. Die Datei ist, wie im obigen Quellcode zu sehen, mit komplettem Pfad angegeben.

Der Dienst läuft unter SYSTEM. SYSTEM hat auf den Zielordner volle Zugriffsrechte.

Ich bin jetzt dabei, ein kleines VCL-Programm zu schreiben, dass vom Dienst per ShellExecute aufgerufen wird, mal sehen, ob das besser funktioniert.

Anmerkung: Was mich halt verwirrt, ist dass im gleichen Thread (TResultT) per FastReport PDF-Dateien erzeugt werden (auch mehrere hintereinander), die im gleichen Verzeichnis landen - das funktioniert einwandfrei, nur Excel (und in Fortsetzung von dem Ganzen) und Outlook weigern sich zu kooperieren. Evtl. muss dafür später ein Tray-Programm her.


Grüße
Frank

Sir Rufo 24. Aug 2014 19:33

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Dein Resourcenschutzblock und Exceptionblock sind falsch. So ist das richtig:
Delphi-Quellcode:
function TResultT.TestExcelfile(intCustID: integer; strResultFile: string): boolean;
var
  strFile   : string;
  strPath   : string;
  strFileName: string;
  XLSFile   : TXLS;
begin
  Result := True;

  strFileName := 'D:\Ausgabe\test.xls';
  try
    XLSFile          := CreateXLS(PAnsiChar(strFileName),xlsfmcreate);
    try
      XLSFile.Creator  := PAnsiChar('Test99');
      XLSFile.Font.Name := (PAnsiChar('Segoe UI'));
      XLSFile.WorkSheetAdd(PAnsiChar('Stichprobenergebnis'));

       // Kopfdaten
      XLSFile.Write(0,1,1, PAnsiChar('Test'));
      XLSFile.Write(0,1,3, PAnsiChar('Kunde'));

      XLSFile.Write(0,2,3, PAnsiChar(GetCustNameByID(intCustID, conData)));

      XLSFile.Write(0,1,4, PAnsiChar('Stichprobe vom:'));

      XLSFile.Write(0,1,5, PAnsiChar('Prüfer/in:'));

      XLSFile.Write(0,1,6, PAnsiChar('Vereinbartes Qualitätsniveau:'));

      XLSFile.WriteFile;
    finally
      XLSFile.Free;
    end;
  except
    on E:Exception do
      begin
        WriteLog('Fehler bei Erzeugen Excel-Tabelle, Meldung: ' + E.Message, 1);
        Result := False;
      end;
  end;
end;

marky522 25. Aug 2014 10:02

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo Frank,

ich bitte Dich noch mal genau zu überprüfen, ob du die USbiff8.dcu oder eine der DLLs verwendest.

Es sieht für mich nämlich so aus, als würdest Du jetzt die DLL Usbiff8.dll verweden - zumindest wäre dann Dein Code stimmig.
Bei dieser Variante verbleibt die Instanz der Klasse nicht in der DLL, sondern es wird auch in deinem Code direkt angesprochen.

Wenn Du eine DLL verweden willst, empfehle ich Dir die Usbiff8V2.dll, bei der die Instanz in Klasse in der DLL verbleibt, oder
Du probiert die Variante aus Test.zip ganz ohne DLL.

Bitte beachte, dass die Schnittstellen bei allen drei Varianten sich geringfügig unterscheiden!

MfG

Markus

FBrust 25. Aug 2014 17:48

AW: Excel-Datei in Dienst + Thread erzeugen und speichern
 
Hallo Markus,

es funktioniert :party:

ich habe jetzt (nach einem erneuten tiefen Blick in das Testprogramm) die usbiff8.dcu in die Uses-Klausel des Dienstes aufgenommen und die Testfunktion so umgeschrieben:

Delphi-Quellcode:
   
function TResultT.TestExcelfile(intCustID: integer; strResultFile: string): boolean;
var
  strFile: string;
  XLSFile: TXLSExport;
begin
  Result := True;

   strFile := 'D:\Ausgabe\Test.xls';

   XLSFile:= TXLSExport.Create(strFile,xlsfmcreate);
   XLSFile.Creator := AnsiString('Test');
   /////// FÜLLMUSTER //////////
   XLSFile.WorkSheetAdd(AnsiString('Füllmuster'));
   XLSFile.SetColWidth(0,0,20);
   XLSFile.SetColWidth(0,1,20);
   XLSFile.Pattern.Style := TXLSPatternstyle(0);
   XLSFile.Font.Name:=AnsiString('Times New Roman');
   XLSFile.Write(0,0,0,AnsiString('test:'));

   XLSFile.Free;
end;
Dies wäre nach meinem Verständnis die Variante ohne DLL.

Die obige Funktion macht jetzt auch, was sie soll, d. h. es wird eine test.xls im angegebenen Verzeichnis erzeugt, die auch lesbar ist :thumb:

Jetzt muss ich sie nur noch so ausbauen, dass sie die geforderten Daten enthält, aber das sollte machbar sein.

Jedenfalls nochmals vielen Dank für Euer aller Mühe (wg. "Mail an Outlook in Dienst und Thread übergeben" melde ich mich dann in einem neuen Thread 8-)).

Grüße
Frank


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:56 Uhr.

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