Problem:
I have made a script that sends a mail through ComObject and it works like a charm when i use it as user, but not if i execute it with Task Scheduler.
What have i done? I have changed ComObjActive to ComObjCreate.
Code:
The body is correct and contains HTML text.
Myself = %AUMailAdress%
Recipient := Myself
Subject := Oi
Body := Ci
Recipient5 = Hidden#gmail.com
DocumentLocation = %SourceDir%\Tooloutput\AutoProjectsLog\Faults
olMailItem := 0
MailItem := ComObjCreate("Outlook.Application").CreateItem(olMailItem)
olFormatHTML := 2
MailItem.BodyFormat := olFormatHTML
MailItem.Subject := Subject
MailItem.HTMLBody := Body
Recipient := MailItem.Recipients.Add(Recipient)
Recipient.Type := 1
Loop, %DocumentLocation%\*.*
MailItem.Attachments.Add(A_LoopFileLongPath)
MailItem.cc := Recipient5
MailItem.Send
ExitApp
return
Issue:
The following error i get when it tries to make a ComObject.
Error: 0x80080005 - Server execution failed
Line: #
---> 674: MailItem: = ComObjCreate ("Outlook.Application"). CreateItem (olMailItem)
Continuous running the script?
I just struggled with this about 3 hours, trying many things, including this method:
MSDN Method | ",512"
At the end, I just ran Outlook as administrator and all started working properly, using this code:
outlookApp := ComObjCreate("Outlook.Application")
MailItem := outlookApp.CreateItem(0)
MailItem.Display
System: Windows 10 x64
Outlook 2019
AHK 1.1.33.02 ran as administrator
Related
I use Delphi 7 IDE. I want to run .exe from the command line with parameters (to be defined).
How can I know if my app is running with the CMD command?
How can I read the parameter with source code?
How can I know if my app is running with the CMD command?
You can't, nor do you ever need to. If your project is a console app, then the .exe is always run inside of a console window process. If the project is not a console app, then there is no console, but you can create one if needed using AllocConsole(). Any process, whether it is a console app or not, can receive command-line parameter, though.
How can I read the parameter with source code?
Use the ParamCount() and ParamStr() functions in the System unit, or the FindCmdLineSwitch() function in the SysUtils unit.
Or, use GetCommandLine() in the Windows unit and parse the raw command-line data yourself.
var
bHideForm = boolean ;
begin
bHideForm := False;
bHideForm := ParamCount > 0;
if bHideForm then
bMetadataExport := GetParamValue(C_MENU, sMetaDataMenu);
{--------------------- function GetParamValue ----------------------------- }
function GetParamValue(aParamName: string; out oParamValue: string): Boolean;
var
i: Integer;
s: string;
begin
Result := False;
for i := 1 to ParamCount do
begin
s := ParamStr(i);
Result := pos(aParamName, UpperCase(s)) = 1;
if Result then
begin
oParamValue := copy(s, length(aParamName)+2, MaxInt);
Exit;
end;
end;
end;
end;
I'm trying to build a service that downloads some log files using SFTP and imports them to the database.
Because Delphi doesn't come with SFTP components, I have created a BAT file to download the logs using WinSCP
DownloadLogs.bat:
WinSCP.com < DownloadLogs.commands
DownloadLogs.commands:
open sftp://root:password#myserver.com
option confirm off
get -delete /var/lib/3cxpbx/Instance1/Data/Logs/CDRLogs files
exit
This is my service:
procedure TsrvCentralita.ServiceExecute(Sender: TService);
const SecondsBetweenExecutions = 10;
var Counter: integer;
dmLogs: TdmLogs;
begin
Counter := 0;
while not Terminated do begin
Inc(Counter);
if Counter > SecondsBetweenExecutions then begin
Counter := 0;
dmLogs := TdmLogs.Create(Self);
try
if dmLogs.DownloadLogs then dmLogs.ImportLogs;
finally
dmLogs.Free;
end;
end;
Sleep(1000);
ServiceThread.ProcessRequests(False);
end;
end;
And this is how I call the BAT file:
function ExecAppWait(AppName: string; Params: string = ''; Directory: string = ''; Hidden: boolean = False): Boolean;
var ShellExInfo: TShellExecuteInfo;
begin
FillChar(ShellExInfo, SizeOf(ShellExInfo), 0);
with ShellExInfo do begin
cbSize := SizeOf(ShellExInfo);
fMask := see_Mask_NoCloseProcess;
Wnd := Application.Handle;
lpFile := PChar(AppName);
lpDirectory := PChar(Directory);
lpParameters := PChar(Params);
if Hidden then nShow := sw_Hide
else nShow := sw_ShowNormal;
end;
Result := ShellExecuteEx(#ShellExInfo);
if Result then
while WaitForSingleObject(ShellExInfo.HProcess, 100) = WAIT_TIMEOUT do begin
Application.ProcessMessages; // give processor time to other tasks
if Application.Terminated then
Break;
end;
end;
function TdmLogs.DownloadLogs(Hidden: boolean = True): boolean;
var Path: string;
begin
Path := TPath.Combine(TPath.GetDirectoryName(Application.ExeName), 'SFTP');;
ExecAppWait(TPath.Combine(Path, 'LogsCentralita.bat'), '', Hidden);
Result := Length(TDirectory.GetFiles(TPath.Combine(Path, 'Files'), '*.log')) > 0
end;
When I debug the DownloadLogs function on my application, it works fine, but when running as a service it freezes. Do you know what is wrong ?, shouldn't I be able to call CMD.exe from a service ?.
Thank you.
update
Following Martin Prikryl's answer I now execute WinSCP this way:
function TdmCentralita.DownloadLogs(SaveOutput: boolean = False): boolean;
var IniFile: TIniFile;
Path, Params, User, Password, Server, Hostkey, RemotePath: string;
begin
IniFile := TIniFile.Create(TPath.ChangeExtension(GetModuleName(HInstance), '.ini'));
Server := IniFile.ReadString('Centralita', 'Servidor', '');
Hostkey := IniFile.ReadString('Centralita', 'Hostkey', '');
User := IniFile.ReadString('Centralita', 'Usuario', 'root');
Password := DecryptStr(IniFile.ReadString('Centralita', 'Password', ''), 223);
RemotePath := IniFile.ReadString('Centralita', 'PathRemoto', '');
IniFile.Free;
while (RightStr(RemotePath, 1) = '\') or (RightStr(RemotePath, 1) = '/') do RemotePath := Copy(RemotePath, 1, Length(RemotePath) - 1);
RemotePath := RemotePath + '/*.log';
Path := TPath.Combine(TPath.GetDirectoryName(GetModuleName(HInstance)), 'SFTP');
if not TDirectory.Exists(TPath.Combine(Path, 'files')) then TDirectory.CreateDirectory(TPath.Combine(Path, 'files'));
Params := '/ini=null /command "open sftp://' + User + ':' + Password + '#' + Server + ' -hostkey=""' + Hostkey + '""" "option confirm off" "get -delete ' + RemotePath + ' files\*" "exit"';
if SaveOutput then Params := Params + ' /log="' + Path + '\Log.txt" /loglevel=0';
ExecAppWait('WinSCP.com', Params, Path, True);
Result := Length(TDirectory.GetFiles(TPath.Combine(Path, 'Files'), '*.log')) > 0
end;
Your script does not contain SSH host key. And due to the strange way you provide the commands (an input redirection instead of /script or /command switches), WinSCP starts in an interactive mode. So it prompts for hostkey verification, and hangs.
Add -hostkey switch to your open command. See:
Verifying the host key in script
My script works fine when executed manually, but fails or hangs when run by Windows Scheduler, SSIS or other automation service. What am I doing wrong?
And use /script or /command switches to make WinSCP abort on any problem, instead of hanging.
You should also read the batch file output for better error handling in the future.
I am using Delphi 2009, Indy ver 10.5498 together with libeay32.dll and ssleay32.dll from the zip file openssl-1.0.2r-i386-win32 at https://indy.fulgan.com/SSL/.
Range checking is turned on in project options and not turned off anywhere.
Using the code below, which I generated with Remy's help from this post, I can upload data to an API on a server via https when running in the IDE with debugging turned on or from a compiled exe generated with debugging tuned on.
However, if I build a release version then whether I run it through the IDE or as an exe I get a range check error on the line result := HttpClient.Post(THE_URL, FormData);
The params list simply contains the to, from, subject, body etc and there is no attachment in the filenames list ie filenames.Count = 0. U_GeneralRoutines.TheFileStoreFolder is simply a folder inside ProgramData where the SSL DLL's are stored.
As the debugger didn't catch this I put in the two showmessage lines before and after the call. When built as debug, both messages get shown and the post succeeds. When built as release the first one gets displayed and then I get the range check error.
I don't suppose there is a bug in the POST code, so what can be going wrong?
function UploadToAPI(params, filenames: TStrings): string;
var
HttpClient: TIdHttp;
IdSSLIOHandler: TIdSSLIOHandlerSocketOpenSSL;
FormData : TIdMultiPartFormDataStream;
i : integer;
PathToSSLlibraries : string;
begin
FormData := TIdMultiPartFormDataStream.Create;
HttpClient:= TIdHttp.Create;
IdSSLIOHandler:= TIdSSLIOHandlerSocketOpenSSL.Create;
PathToSSLlibraries := IncludeTrailingPathDelimiter(U_GeneralRoutines.TheFileStoreFolder);
IdOpenSSLSetLibPath(PathToSSLlibraries); //set path to libeay32.dll and ssleay32.dll in the common ProgramData folder
HttpClient.IOHandler := IdSSLIOHandler;
HttpClient.Request.CustomHeaders.FoldLines := true ;
try
for i := 0 to params.Count - 1 do
FormData.AddFormField(params.Names[i], params.ValueFromIndex[i]);
for i := 0 to filenames.Count - 1 do
FormData.AddFile('attachment', filenames[i]); //works with ver 10.5498 but not with 10.2.5
//add authorisation header
HttpClient.Request.CustomHeaders.Add('Authorization:Basic ' + ATHORISATION_STR); //byte64 encoding of the api key
HttpClient.ProtocolVersion := pv1_1; //get the full server response which allows for just one try-except
HttpClient.HTTPOptions := HttpClient.HTTPOptions + [hoKeepOrigProtocol, hoNoProtocolErrorException, hoWantProtocolErrorContent];
try
showmessage('about to post');
result := HttpClient.Post(THE_URL, FormData); //post to the api
showmessage('posted');
except
on E: Exception do
begin
result := E.ClassName + ': ' + E.message;
raise;
end;
end; //try
finally
FormData.Free;
IdSSLIOHandler.free;
HttpClient.free;
end;
I appreciate that this type of scenario is often caused by unintialised variables in the release version that would get automatically initialised when in the IDE / debug. But all the variables in my procedure do seem to be getting initialised before the call to POST.
I want run phantonJS executable from TProcess wuth my JS and some parametrs for it. But TAsyncProcess ignore my command line params.
PhandomJS docs says, that I must run my script in that order:
phantomjs.exe [phantom opts] jsfile.js [jsfile opts]
In code:
fProc := TAsyncProcess.Create(nil);
fProc.Options := [poNoConsole, poStderrToOutPut];
fProc.ShowWindow := swoNone;
fproc.StartupOptions := [suoUseShowWindow];
fProc.OnTerminate := #privOnProcTerminated;
fDebugFile := '';
...
fProc.CurrentDirectory := ExtractFilePath(fExecutable);
fProc.Executable := fExecutable;
fproc.Parameters.Add(fPhantomScript);
fproc.Parameters.Add(IntToStr(fPort));
fproc.Parameters.Add(fHost);
fproc.Parameters.Add(fDebugFile);
fProc.ShowWindow := swoShowNormal;
fproc.StartupOptions := fproc.StartupOptions + [suoUseShowWindow];
if not isRunning then
begin
fProc.Execute;
Logger.Send('phantonJS launched.');
Result := True;
end
The executable is launching, but I don't see, that's parametrs was applyed to process (via System Explorer), also script don't work as it must.
Why TAsyncProcess ignore my params?
How to fix that?
Lazarus 1.4.4 from web-site.
Target OS: Windows
I have tested that this generally works with Lazarus. I assume that the syntax of the parameters passed was wrong.
As #Nested Type has said: You don't need to quote parameters. TProcess does that for you.
I have a simple mobile app, that takes a series of photos and sends it via SendStream() to the connected Profile.
myTetherAppProfile.SendStream(myTetherManager.RemoteProfiles[idConnected],
'ImageData',
bmpStreamData);
The occurring problem here is that the receiver-app doesn't get all the image-streams depending on the connection-strength (The ResourceReceived-Event isn't triggered on the receiver-app).
This would be no problem if I get a response that the delivery failed. But I don't get this (SendStream() returns "True")
Is there a possibility other than implementing a "please answer with another message if you received my image"-function to achieve stable transmissions even with bad connection? Or is App-Tethering by default designed to be lossy?
Also after a big stack of images I sometimes get the "connection reset by peer"-error. (I'm not sure if this error is related to the actual problem, so I preferred posting it.)
Looking at the relevant code from System.Tether.AppProfile (XE8 version), it appears to be a bug. See my inline comments below. Please report to https://quality.embarcadero.com
function TTetheringAppProfile.SendStream(const AProfile: TTetheringProfileInfo; const Description: string;
const AStream: TStream): Boolean;
var
LProfileInfo: TTetheringProfileInfo;
LConnection: TTetheringConnection;
LCommand: TTetheringCommand;
begin
if not FindProfile(AProfile.ProfileIdentifier, LProfileInfo) then
raise ETetheringException.CreateFmt(SNoProfile, [AProfile.ProfileIdentifier]);
CheckProfileIsConnected(AProfile);
LConnection := GetConnectionTo(AProfile);
TMonitor.Enter(LConnection);
try
LCommand := LConnection.Protocol.SendCommandWithResponse(SendStreamCommand, Version, Description);
if LCommand.Command = SendStreamOkResponse then
begin
Result := LConnection.Protocol.TransferStream(AStream);
if Result then
begin <-- Result here is guaranteed to be True
LCommand := LConnection.Protocol.ReceiveCommand;
if LCommand.Command = SendStreamContentOKResponse then
Result := True; <-- Sets Result to True if succeeds,
<-- but nothing to set Result to False if call failed.
end;
end
else
Result := False;
finally
TMonitor.Exit(LConnection);
end;
end;