Send data to Delphi server - html

I'm making a Delphi XE5 VCL Forms Application with a TIdHTTPServer on the main form and a CommandGet of the IdHTTPServer procedure:
procedure TForm1.IdHTTPServerCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var pageContent: TStringList;
begin
if pos('&command=add', ARequestInfo.UnparsedParams) > 0 then
begin
pageContent:= TStringList.Create;
try
pageContent.Add('<html>');
pageContent.Add('<head>');
pageContent.Add('<title>Profile</title>');
pageContent.Add('</head>');
pageContent.Add('<body>');
pageContent.Add('<input id="subjects" type="text"/>');
pageContent.Add('<input id="Add" type="button" onclick="sendData()"/>');
pageContent.Add('</body>');
pageContent.Add('</html>');
AResponseInfo.ContentText := pageContent.Text;
finally
pageContent.Free;
end;
end;
end;
My question is how the user input is send to the server when the user clicks the button 'Add'.

With this HTML, the client (web browser) will not send any data because there is no HTML form element present.

Related

Delphi IE11 instance, OleSysErrorOLE A system shutdown has already been scheduled/8150002E

I have a Delphi app which prints HTML to PDF by opening the document in IE11 in the background and using the default PDF printer to print to PDF. This process works fine.
The issue is the following:
(Example I want to print 5 HTML documents to PDF.)
On the first run, it processes the first document fine, but then skips the rest with the following errors:
On Win Server 2016:
EOleSysErrorOLE A system shutdown has already been scheduled.
On Windows 10:
EOleSysErrorOLE error 8150002E
Then I wait until "iexplore.exe" closes, which takes a few sec.
From then, it processes all documents just fine regardless the number of the documents.
If I do not use the app for a long time (approx a day), it does the same as above.
It skips on the first run, then waits a few seconds and then us fine.
I tried to use OleVariant and IWebBrowser2, but both have the same outcome.
I close the Object with .Quit. (see in code below). I also tried Unassigned, Free, setting the Object to Null before creating a new object. None of them worked. Same outcome.
Here are a few thing which I tried as a workaround:
If I do not use .Quit, it works fine, but obviously won't close any iexplore.exe.
Also, if I open an IE window (GUI) and minimize it, the HTML-PDF process works fine.
I also tried to call to create a background IE object on TMainForm.FormCreate() when the app starts, and it works as well.
When it gets to the HTML-PDF process, it creates a new IE background object (additional "iexplore.exe") and closes it by leaving the one created on FormCreate().
I would like to figure out why it just cannot create and close an object fast enough on the first run (without having an IE opened or without using .Quit).
Here is the code:
Note: The program also writes some stuff to the registry, but I cut some lines for simplicity (I also might cut a few end here and there. Note that the function works fine apart than the issue above).
function THTMLMergeDocument.FilePrint: boolean;
var
BrowserObject: OleVariant;
ie : IWebBrowser2;
vaIn, vaOut: OleVariant;
OldHeader, OldFooter, OldPrinterName: String;
OldOrientation: Integer;
Registry : TRegistry;
ST, TOutVal: TDateTime;
sUrl : string;
Flag, TargetFrameName, PostData, Headers : OleVariant;
begin
result := false;
try
if fPrinterName = VTPrint.GetDefaultPrinter then
begin
try
//Tried OleVariant
{
BrowserObject := Unassigned;
BrowserObject := CreateOleObject('InternetExplorer.Application');
BrowserObject.Silent := true;
BrowserObject.Visible := false;
BrowserObject.Navigate('file:\\'+DocumentFileName);
BrowserObject.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER);
while BrowserObject.Busy or BrowserObject.ReadyState <> READYSTATE_COMPLETE do
begin
Application.ProcessMessages;
end;
}
//Tried IWebBrowser2
ie := CoInternetExplorer.Create;
sUrl := 'file:\\'+DocumentFileName;
ie.Navigate(sUrl, Flag, TargetFrameName, PostData, Headers);
ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER,vaIn,vaOut);
while ie.ReadyState <> READYSTATE_COMPLETE do
begin
Application.ProcessMessages;
end;
if (PDFOutput) then
begin
ST := Now;
TOutVal := EncodeTime(0,DocumentServerOptions.PDFConverterTimeOutInterval,0,0);
while not PDF.Completed and (Now-ST<TOutVal) do
Application.ProcessMessages;
if PDF.Completed then
result := true
else
WinWordLogProc('ERROR: No response received from PDF converter');
end
else
begin
result := true;
end;
ie.Quit; //close IWebBrowser2 object
BrowserObject.Quit; //close OleVariant object
except on E: Exception do
WinWordLogProc( 'Error class: ' + E.ClassName + #13 + E.Message);
end;
end
else
begin
WinWordLogProc('Error setting default printer to '+fPrinterName);
end;
finally
VTPrint.SetDefaultPrinter(OldPrinterName);
end;
`
I suspect that it has something do do with the IE object handling, but I'm not sure, hence asking for help here.
The browser's Navigate() method is asynchronous. You should wait for the document to finish loading before you then call ExecWB() to print the document, not after calling it.

Send mail with special characteres and Indy

I try to send email. In the body of TIdMessage I have a string with 'ménage'. After the IdMessage is send via an IdSMTP. If my program run under Windows (compiled with Lazarus), no problem. If my program is compiled and run under Linux, I get 'propreté'. I have try meDefault, meMIME and mePlainText for the encoding property of the IdMessage, nothing to do I allways have the error.
Thanks for your help.
I am with Lazarus 2.0.6 on Raspberry PI. I have changed a little bit my code, now the special characteres are ok inside the message, but not in the subject of my email!!??
IdMessage.Clear;
IdMessage.IsEncoded := True;
IdMessage.ContentTransferEncoding := 'base64';
IdMessage.Encoding := meMime;
IdMessage.ContentType := 'text/plain';
IdMessage.Charset := 'UTF-8';
IdMessage.Date:=Now; //Date de l'envoi du message
IdMessage.Subject:='Propreté'; //Sujet du message
IdMessage.Priority:=mpNormal; //Priorité du message
Thanks

How to show relative-path images in TWebBrowser?

I am using TWebBrowser in DesignMode (Doc.DesignMode := 'On') to compose a HTML document. There is no document (HTML file on disk) loaded in TWebBrowser. I create the document from zero directly in TWebBrowser. The html code will be extracted from TWebBrowser and saved as c:/MyProjects/SomeHtmlName.html.
The problem is that it won't show images I insert if they have relative path.
More exactly, if I paste this code in the WebBrowser it will instantly display the image:
<IMG src="file:///c:/MyProjects/resources/R.PNG">
However, if I enter:
<IMG border=0 src="resources\R.PNG">
<IMG border=0 src="resources/R.PNG"> <---- preferred so it will work on Linux
it will display an image placeholder instead of the actual image.
I need relative paths so the web site will still work if I change the root path OR if I upload it on FTP.
procedure TForm1.Button1Click(Sender: TObject);
begin
LoadDummyPage;
SetHtmlCode('<img src="resources/test_img.PNG">');
Memo1.Text:= GetHtmlCode;
end;
function TForm1.LoadDummyPage: Boolean;
const FileName: string= 'c:\MyProject\_ONLINE WEB SITE\dummy.html';
begin
if not Assigned(wbBrowser.Document)
then wbBrowser.Navigate('about:blank');
Result := FileExists(FileName);
if Result
then wbBrowser.Navigate('file://' + FileName)
else Caption:= 'file not found';
end;
procedure TForm1.SetHtmlCode(const HTMLCode: string);
var
Doc: Variant;
begin
if not Assigned(wbBrowser.Document)
then wbBrowser.Navigate('about:blank');
Doc := wbBrowser.Document;
Doc.Write(HTMLCode);
Doc.Close;
Doc.DesignMode := 'On';
WHILE wbBrowser.ReadyState < READYSTATE_INTERACTIVE
DO Application.ProcessMessages;
Doc.body.style.fontFamily := 'Arial';
Doc.Close;
end;
function TForm1.GetHtmlCode: string; { Get the HTML code from the browser }
var
Doc: IHTMLDocument2;
BodyElement: IHTMLElement;
begin
if Assigned(wbBrowser.Document) and (wbBrowser.Document.QueryInterface(IHTMLDocument2, Doc) = S_OK) then
begin
BodyElement := Doc.body;
if Assigned(BodyElement) then
Result := BodyElement.innerHTML;
end;
if Result > ''
then Result := StringReplace(Result, '="about:', '="', [rfReplaceAll, rfIgnoreCase]); { Fix the 'How stop TWebBrowser from adding 'file:///' in front of my links' bug }
end;
You need to pre-load a valid HTML "template" string/stream including the BASE tag where you set the desired path (with trailing slash) e.g. "file:///c:/MyProjects/".
And switch to edit mode, where your images SRC should be relative e.g. "resources/R.PNG". Your final extracted HTML ater editing should be the body.innerHTML or body.outerHTML (whatever you need). You can even take the whole document source (google it).
Wrap the extracted source with valid HTML/Body WITHOUT the BASE tag and save to disk at c:\MyProjects.
but the code resulted for IMG SRC is full path!
Nothing much you can do about it. this is how the DOM represent the HTML - it's not necessary the HTML source code. this behavior is not consistent. and also depend on how you insert images (I do not use execCommand and have my own dialog and insert my own html code). You need to manually replace the extracted source "file:///c:/MyProjects/" with empty string. at least, this is how I do it.
Edit: You don't need to Navigate() to an external file. you can write the "template"/"empty" HTML via document.write(HTML).
Try this:
const
HTML_TEMPLATE = '<html><head><base href="file:///%s"></head><body style="font-family:Arial">%s</body></html>';
procedure TForm1.LoadHTML(HTMLCode: string);
var
Doc: Variant;
HTML, Path: string;
begin
Path := 'D:\Temp\';
HTML := Format(HTML_TEMPLATE, [Path, HTMLCode]);
WebBrowser1.Navigate('about:blank');
Doc := WebBrowser1.Document;
Doc.Write(HTML);
Doc.Close;
Doc.DesignMode := 'On';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
LoadHTML('<b>Hello</b><img SRC="resources/1.png">');
end;
procedure TForm1.Button2Click(Sender: TObject);
var
Doc: IHTMLDocument2;
begin
Doc := WebBrowser1.Document as IHTMLDocument2;
if Assigned(Doc) then
begin
ShowMessage(Doc.body.innerHTML);
end;
end;
The output for me is: <B>Hello</B><IMG src="resources/1.png">. in some cases the src might contain the full path. I can't 100% be sure to when this happens. but you need to be ready to deal with this situation by manually replacing the path. there is no conclusive documentation about this so I always deal with this issue in any case.
I suggest to use a small embedded web server such as Internet Direct (Indy) TIdHttpServer, which is able to serve all HTTP requests in a standard way. This removes all potential file system trouble.

Delphi7 Indy HTTPServer not getting form parameters

I have an Indy 10 IdHTTPServer in a Windows application which serves a virtual HTML form with two text boxes and a submit button. When the button is pressed in the browser I am not seeing any form params returned to the server.
Note that this is a bit of proof of concept code which will be used to make a windows service respond to button presses in a web form.
The HTML form is like this:
<form action="http://<addressofsite>/" method="post">
First name:<br>
<input type="text" name="firstname" value="Mickey"><br>
Last name:<br>
<input type="text" name="lastname" value="Mouse"><br><br>
<input type="submit" value="Submit">
</form>
in the Delphi code I have this:
procedure TForm1.HTTPServer1CommandGet(AThread: TIdPeerThread;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
...
if ARequestInfo.Command = 'POST' then
begin
{******* POSTS ***************}
Memo1.Text := ARequestInfo.RawHTTPCommand;
end;
end;
I have tried various bits of the ARequestInfo structure but whatever I try all I see when the button is pressed in the browser is:
POST / HTTP 1.1
No params appear to be passed.
I'm obviously doing something wrong, so please can someone point out my idiocy.
Update:
As pointed out by The Arioch below, I should have checked that the browser is actually sending the data - so using Chrome developer tools I examined the headers, the results of which are:
Response Headers
Connection:close
Content-Type:text/html
Server:Indy/10.0.52
Request Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image /webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, br
Accept-Language:en-GB,en-US;q=0.8,en;q=0.6
Authorization:Basic YWRtaW46cGFzcw==
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:31
Content-Type:application/x-www-form-urlencoded
Host:127.0.0.1:8091
Origin:http://127.0.0.1:8091
Referer:http://127.0.0.1:8091/main
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36
Form Data
firstname:Mickey
lastname:Mouse
So the browser is definitely sending the form data.
The raw encoded form data is stored in the ARequestInfo.FormParams and ARequestInfo.UnparsedParams properties.
If TIdHTTPServer.ParseParams is true (which it is by default), the decoded form data is stored in the ARequestInfo.Params property, eg:
procedure TForm1.HTTPServer1CommandGet(AThread: TIdPeerThread;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
FirstName, LastName: string;
begin
...
if (ARequestInfo.CommandType = hcPOST) and
IsHeaderMediaType(ARequestInfo.ContentType, 'application/x-www-form-urlencoded') then
begin
FirstName := ARequestInfo.Params.Values['firstname'];
LastName := ARequestInfo.Params.Values['lastname'];
...
end;
end;
Note that TIdHTTPServer is a multi-threaded component. The various events, including OnCommandGet, are fired in the context of worker threads. So, if you need to touch UI controls, like your TMemo, you must synchronize with the main UI thread, eg:
procedure TForm1.HTTPServer1CommandGet(AThread: TIdPeerThread;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
...
if (ARequestInfo.CommandType = hcPOST) and
HeaderIsMediaType(ARequestInfo.ContentType, 'application/x-www-form-urlencoded') then
begin
TThread.Synchronize(nil,
procedure
begin
Memo1.Text := ARequestInfo.Params.Text;
end
);
...
end;
end;
Also, 10.0.52 is an outdated version of Indy. The current version (at the time of this writing) is 10.6.2.5384.

TRichMemo - LoadRichText not working

I am trying to save the contents of a TRichMemo to TMemoryStream, and then be able to load the formatted data back from the stream into the rich memo.
The problem is LoadRichText is failing for some reason. I know the data is been saved to my stream because I can actually save it fo file as rtf and view it externally.
This is basically what I have:
var
FMyStream: TMemoryStream;
To save:
RichMemo1.SaveRichText(FMyStream);
To load:
FMyStream.Seek(0, soBeginning);
if not RichMemo1.LoadRichText(FMyStream) then
raise Exception.Create('Failed to load data from stream.');
As I said the data is saved to stream correctly, but trying to load into the rich memo is hitting my exception everytime.
What could be the problem?
The code for the LoadRichText function is:
function TCustomRichMemo.LoadRichText(Source: TStream): Boolean;
begin
if Assigned(Source) and HandleAllocated then begin
Result := TWSCustomRichMemoClass(WidgetSetClass).LoadRichText(Self, Source);
if not Result and Assigned(RTFLoadStream) then begin
Self.Lines.BeginUpdate;
Self.Lines.Clear;
Result:=RTFLoadStream(Self, Source);
Self.Lines.EndUpdate;
end;
end else
Result := false;
end;
and SaveRichText code:
function TCustomRichMemo.SaveRichText(Dest: TStream): Boolean;
begin
if Assigned(Dest) and HandleAllocated then begin
Result := TWSCustomRichMemoClass(WidgetSetClass).SaveRichText(Self, Dest);
if not Result and Assigned(RTFSaveStream) then
Result:=RTFSaveStream(Self, Dest);
end else
Result := false;
end;
Thanks.
Ok, I found the solution to my problem.
At first I created a simple test project and LoadRichText and SaveRichText worked, which meant the problem was within my code somewhere...
My stream is declared in a class in a separate unit. In another form I have my rich memo control, when the form is closed the data is saved to the stream, that part I knew worked because I could save it to file and view it externally.
The problem was when I was creating the form that contains my rich memo, I was calling LoadRichText from the FormCreate event. So I moved it into FormActivate and now it works without error.