I want to print XML document in Delphi, using OleObject. My code:
var
ie : OleVariant;
begin
ie := CreateOleObject('InternetExplorer.Application');
ie.Navigate('doc.xml');
ShowWindow(frmGlowna.ie.HWND, SW_SHOWMAXIMIZED);
ie.Visible := True;
while ie.QueryStatusWB( OLECMDID_PRINT) <> OLECMDF_SUPPORTED + OLECMDF_ENABLED do
Application.ProcessMessages;
ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER,OleVariant(VarAsType($02, varSmallint)), vOut);
ie.Quit;
ie := Unassigned;
end;
Line:
ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER,OleVariant(VarAsType($02, varSmallint)), vOut);
Execute printing command and wait for finish.
Everything is OK, as long as the program does not run again or run the same program twice at the same time.
I get different errors:
Catastrophic failure
or
The object invoked has disconnected from its clients
Please help.
Related
I have been using this code for more than 10 years and it suddenly stopped working.
The program reads a list of pdf files and output a single pdf file containing all the files in the list. It uses gsapi.pas wrapper unit. over the years I have only had to download an updated version gsdll32.dll file from Ghostscript.to be compatible with newer versions of pdf file format. but it is not working anymore
Any ideas of what is going on?
procedure PDFMerge(input : Array of String;output : string);
var
code:integer;
instance:Pointer;
argv:PPChar;
N,I : smallint;
begin
new(instance);
N:=length(input);
setlength(argv,6+N);
code:=gsapi_new_instance(#instance,nil);
if code<>0 then
begin
raise Exception.Create('Impossible to open an instance of ghostscript. Error code: '+IntToStr(code));
end;
try
//try adding dNothing as first parameter argv[1] := pchar('dNothing');
argv[0] := pchar('AppendPdf');
argv[1] := pchar('-dNOPAUSE');
argv[2] := pchar('-dBATCH');
argv[3] := pchar('-dSAFER');
argv[4] := pchar('-sDEVICE=pdfwrite');
argv[5] := pchar('-sOutputFile='+ output);
argv[6] := pchar('-c');
argv[7] := pchar('.setpdfwrite');
argv[8] := pchar('-f');
for I := 0 to N - 1 do
argv[9+I] := pchar(input[I]);
//Call gs32.dll
code := gsapi_init_with_args(instance, length(argv), argv);
if code<0 then
raise Exception.Create('ERROR: init_args: '+IntToStr(code));
gsapi_exit(instance);
gsapi_delete_instance(instance);
finally
end;
end;
I am using Ghostscript V10.0. I found that they have a new interpreter perhaps it has to do with That? Although I did try addidng the switch "-dNEWPDF=false" but no luck. I keep getting error code -100.
Note using the equivalent command line in Ghostscript itself works fine. It seems to me they changed the way the API needs to be called. Whould that be the case?
I would appreciate any help.
I did try addidng the switch "-dNEWPDF=false" but no luck.
In a service running under system account the code below hangs in the TMemIniFile.Create without errors.
If we replace it with TIniFile, it works fine.
It's a Delphi Tokyo 10.2.3 Win32 app running under Windows Server 2012R2. There's no concurrent access to the INI file.
This is the first time (first client) we see this, it has been running fine on many machines.
I have no idea what to look for further. Any ideas?
It 'works' now because we switched to TIniFile, but I'd like to find the cause. From other posts I read here, TINIfile seems to be more finicky than TMemINIfile, my situation is the reverse.
There are no special characters in the INI file and it is created with an ASCII editor.
// This is set in the ServiceCreate:
FIniFileNaam := ChangeFileExt(ParamStr(0),'.INI');
// This is called from the ServiceStart:
procedure TTimetellServiceBase.LeesINI;
var lIniFile : TMemIniFile;
begin
LogMessage(FIniFileNaam, EVENTLOG_INFORMATION_TYPE, cCatInfo, cReadINI); // Logs to event log, we see this
FStartDir := ExtractFilePath(ParamStr(0));
if assigned(FLaunchThreadLijst) then FreeAndNil(FLaunchThreadLijst);
FLaunchThreadLijst := TStringList.Create;
try
if FileExists(FIniFileNaam) then
begin
// Lees waarden uit INI file
lIniFile := TMemIniFile.Create(FIniFileNaam); // This is the call that hangs. The service is unresponsive now.
try
FLaunchThreadLijst.CommaText := lIniFile.ReadString(INISECTIE_SERVICETASKS,'RunIniFiles','');
FMaxTaskDuration := lIniFile.ReadInteger(INISECTIE_SERVICETASKS,'MaxTaskDuration',FMaxTaskDuration);
finally
FreeAndNil(lIniFile);
end;
end;
finally
if (FLaunchThreadLijst.Count = 0) and FileExists(FStartDir + FExeName) then
FLaunchThreadLijst.Add(SDEFAULTTHREADNAME);
LogMessage(Format('FLaunchThreadLijst.CommaText: %s (%d items)',[FLaunchThreadLijst.CommaText,FLaunchThreadLijst.Count]), EVENTLOG_INFORMATION_TYPE, cCatInfo, cLaunchList);
end;
end;
FWIW, INI file contents:
[TASKMANAGER]
RunIniFiles=TTTasks.ini
MaxTaskDuration=2
RestartIniFiles=
KillIniFiles=
i am working on a little byte patching program but i encountered an error.
copying the file before modifying fails with no error, (no copied output is seen) but the file patches successfully.
Here is the Patch Code
procedure DoMyPatch();
var
i: integer;
FileName: string;
input: TFileStream;
FileByteArray, ExtractedByteArray: array of Byte;
begin
FileName := 'Cute1.res';
try
input := TFileStream.Create(FileName, fmOpenReadWrite);
except
begin
ShowMessage('Error Opening file');
Exit;
end
end;
input.Position := 0;
SetLength(FileByteArray, input.size);
input.Read(FileByteArray[0], Length(FileByteArray));
for i := 0 to Length(FileByteArray) do
begin
SetLength(ExtractedByteArray, Length(OriginalByte));
ExtractedByteArray := Copy(FileByteArray, i, Length(OriginalByte));
// function that compares my array of bytes
if CompareByteArrays(ExtractedByteArray, OriginalByte) = True then
begin
// Begin Patching
CopyFile(PChar(FileName), PChar(ChangeFileExt(FileName, '.BAK')),
true); =======>>> fails at this point, no copied output is seen.
input.Seek(i, SoFromBeginning);
input.Write(BytetoWrite[0], Length(BytetoWrite)); =====>>> patches successfully
input.Free;
ShowMessage('Patch Success');
Exit;
end;
end;
if Assigned(input) then
begin
input.Free;
end;
ShowMessage('Patch Failed');
end;
sidenote : it copies fine if i close the filestream before attempting copy.
by the way, i have tested it on Delphi 7 and XE7.
Thanks
You cannot copy the file because you locked it exclusively when you opened it for the file stream, which is why CopyFile fails.
You should close the file before attempting to call CopyFile. Which would require you to reopen the file to patch it. Or perhaps open the file with a different sharing mode.
Some other comments:
The exception handling is badly implemented. Don't catch exceptions here. Let them float up to the high level.
Lifetime management is fluffed. You can easily leak as it stands. You need to learn about try/finally.
You overrun buffers. Valid indices for a dynamic array are 0 to Length(arr)-1 inclusive. Or use low() and high().
You don't check the value returned by CopyFile. Wrap it with a call to Win32Check.
The Copy function returns a new array. So you make a spurious call to SetLength. To copy the entire array use the one parameter overload of Copy.
Showing messages in this function is probably a mistake. Better to let the caller provide user feedback.
There are loads of other oddities in the code and I've run out of energy to point them all out. I think I got the main ones.
I have console Delphi application which works with Excell though OLE.
The examle of code is pretty simple
procedure SaveBaseRecordsToFile(BaseName: string; PaymentRecords: TPaymentRecords);
var
i: integer;
Excel: Variant;
begin
try
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
except
on E: Exception do begin
LogWriter.WriteLog(E.ClassName + ': ' + E.Message);
exit;
end;
end;
try
Excel.Workbooks.Add;
//Excel.Worksheets.Add;
Excel.Worksheets[1].Cells[2, 1].Value := 'Account number';
Excel.Worksheets[1].Cells[2, 2].Value := 'Sum';
for i := 0 to Length(PaymentRecords) - 1 do begin
Excel.Worksheets[1].Cells[i + 3, 1].Value := PaymentRecords[i].NUMBER;
Excel.Worksheets[1].Cells[i + 3, 2].Value := PaymentRecords[i].SUMMA;
end;
Excel.ActiveSheet.Name := 'SHEET1';
Excel.Application.Workbooks[1].SaveAs(ExtractFilePath(ParamStr(0)) + BaseName + '.xls', 56);
Excel.Workbooks.Close;
Excel.Quit;
finally
Excel := Unassigned;
end;
end;
When I run this application in interactive mode (by myself) it wors perfect.
But when I try to run it through standart Microsoft Scheduler I see in log of my applications such records:
21:41:40.523: EOleSysError: Отказано в доступе, ProgID: "Excel.Application" (Access denied from Russian)
If I set in schedule task option "Run with highest privileges" I see in log of my applications such records:
20:12:04.475: EOleException: Метод SaveAs из класса Workbook завершен неверно (SaveAs method of Workbook class finished incorrectly from Russian)
Is there any way to run application with OLE call through Microsort Scheduler?
Or maybe I can work with Excel without OLE (it's pretty simple operations as you could see in my examle)? How can I do it?
The fundamental issue is that Excel needs to run in an interactive desktop. Running through the task scheduler, as you have configured it, runs it in a non-interactive desktop. That's not a supported mode of operation for Excel: http://support.microsoft.com/kb/257757
Your only real hope of success is to create the Excel file without using COM automation of Excel. There are many libraries to help you do that.
In my program, the user completes a form and then presses Submit. Then, a textfile or a random extension file is created, in which all the user's information is written. So, whenever the user runs the application form, it will check if the file, which has all the information, exists, then it copies the information and pastes it to the form. However, it is not working for some reason (no syntax errors):
procedure TForm1.FormCreate(Sender: TObject);
var
filedest: string;
f: TextFile;
info: array[1..12] of string;
begin
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
if FileExists(filedest) then
begin
AssignFile(f,filedest);
Reset(f);
ReadLn(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
Edit1.Text := info[1];
Edit2.Text := info[2];
ComboBox1.Text := info[3];
ComboBox5.Text := info[4];
ComboBox8.Text := info[4];
ComboBox6.Text := info[5];
ComboBox7.Text := info[6];
Edit3.Text := info[7];
Edit4.Text := info[8];
Edit5.Text := info[11];
Edit6.Text := info[12];
ComboBox9.Text := info[9];
ComboBox10.Text := info[10];
CloseFile(f);
end
else
begin
ShowMessage('File not found');
end;
end;
The file exists, but it shows the message File not found. I don't understand.
I took the liberty of formatting the code for you. Do you see the difference (before, after)? Also, if I were you, I would name the controls better. Instead of Edit1, Edit2, Edit3 etc. you could use eFirstName, eLastName, eEmailAddr, etc. Otherwise it will become a PITA to maintain the code, and you will be likely to confuse e.g. ComboBox7 with ComboBox4.
One concrete problem with your code is this line:
readln(info[1], info[2], info[3], info[4], info[5], info[6], info[7],
info[8], info[9], info[10], info[11], info[12]);
You forgot to specify the file f!
Also, before I formatted your code, the final end of the procedure was missing. Maybe your blocks are incorrect in your actual code, so that ShowMessage will be displayed even if the file exists? (Yet another reason to format your code properly...)
If I encountered this problem and wanted to do some quick debugging, I'd insert
ShowMessage(BoolToStr(FileExists(filedest), true));
Exit;
just after the line
filedest := ...
just to see what the returned value of FileExists(filedest) is. (Of course, you could also set a breakpoint and use the debugger.)
If you get false, you probably wonder what in the world filedest actually contains: Well, replace the 'debugging code' above with this one:
ShowMessage(filedest);
Exit;
Then use Windows Explorer (or better yet: the command prompt) to see if the file really is there or not.
I'd like to mention an another possibility to output a debug message (assuming we do not know how to operate real debugger yet):
{ ... }
filedest := ExtractFilePath(ParamStr(0)) + 'User\Identity\IdentityofMyself.txt';
AllocConsole; // create console window (uses Windows module) - required(!)
WriteLn('"' + filedest + '"'); // and output the value to verify
if FileExists(filedest) then
{ ... }