I have a formatted text on a wordpad file(rtf). I'm trying to open it on a richedit on a delphi form. The problem is that the string is in cyrillic(Bulgarian) and it's saved with weird hieroglyphs or whatever those are "Âëåçå ïîòðåáèòåë". Is there a way to transfer/translate the hieroglyphs to the richedit, so they can appear as proper text?
This function I use to check if the file is empty so I can then enter the first rtf tag, or remove the closing tag, so I can add more text in there without breaking the file
function FileIsEmpty(const FileName: String): Boolean;
var
fad: TWin32FileAttributeData;
begin
Result := GetFileAttributesEx(PChar(FileName), GetFileExInfoStandard, #fad) and
(fad.nFileSizeLow = 0) and (fad.nFileSizeHigh = 0);
end;
This is the code I use to format the text and also give it to the file:
procedure FormatLogAndAddToFile(richEditLog : TRichEdit; richEditTextColor : TRichEdit);
var
i : integer;
s, c, finalText : string;
sString : TStringList;
begin
with frmMain do
begin
sString := TStringList.Create;
sString.LoadFromFile('C:\Users\lyuben\Desktop\Lyuben Airport Delphi\Log File\TestFormating.rtf');
if Pos('{\rtf}', sString.Strings[0]) <> 0 then
begin
sString.Delete(0);
end
else
begin
sString.Delete(sString.Count - 1);
end;
sString.SaveToFile('C:\Users\lyuben\Desktop\Lyuben Airport Delphi\Log File\TestFormating.rtf');
sString.free;
AssignFile(logFile, 'C:\Users\lyuben\Desktop\Lyuben Airport Delphi\Log File\TestFormating.rtf');
Append(logFile);
if FileIsEmpty('C:\Users\lyuben\Desktop\Lyuben Airport Delphi\Log File\TestFormating.rtf') = True then
begin
WriteLn(logFile, '{\rtf\ansi\ansicpg1252\deff0\nouicompat{\fonttbl{\f0\fnil\fcharset0 Calibri;}}');
end;
for i := 0 to richEditLog.Lines.Count do
begin
s := richEditLog.Lines[i];
c := richEditTextColor.Lines[i];
if c = 'blue' then
begin
finalText := '{\colortbl ;\red0\green128\blue255;\red255\green0\blue0;}' +
'\viewkind4\uc1 \pard\sa200\sl276\slmult1\cf1\f0\fs32\lang9 ' + s + '\cf2\par';
end
else if c = 'red' then
begin
finalText := '{\colortbl ;\red255\green0\blue0;}' +
'\viewkind4\uc1 \pard\sa200\sl276\slmult1\cf1\f0\fs32\lang9 ' + s + '\par';
end
else if c = 'green' then
begin
finalText := '{\colortbl ;\red0\green128\blue128;\red255\green0\blue0;}' +
'\viewkind4\uc1 \pard\sa200\sl276\slmult1\cf1\f0\fs32\lang9 ' + s + '\cf2\par';
end;
WriteLn(logFile, finalText);
end;
WriteLn(logFile, '}');
CloseFile(logFile);
end;
end;
This is the code I use to add the log lines to the file. I also have little bit of code that checks if the file has lines with a date that is entered on a TDateEdit, so I can only get log from the date I've entered.
procedure OpenLogInRichEdit(dateFilter : Boolean; searchDate : tDate);
var
sTime : string;
dateExists : Boolean;
I : integer;
begin
with frmMain do
begin
dateExists := false;
frmLogSearch.tLogRichEdit.Clear;
frmLogSearch.tLogRichEdit.Lines.LoadFromFile('C:\Users\lyuben\Desktop\Lyuben Airport Delphi\Log File\TestFormating.rtf');
sTime := DateTimeToStr(searchDate);
if dateFilter then
begin
for I := 0 to frmLogSearch.tLogRichEdit.Lines.Count do
begin
if Pos(sTime, frmLogSearch.tLogRichEdit.Lines[i]) <> 0 then
begin
frmLogSearch.tLogRichEdit.Lines.Delete(i);
dateExists := True;
end;
end;
if dateExists = false then
begin
ShowMessage('No log from this day!');
end;
end;
end;
end;
This is how I add the text to the richedits I use later for the procedure FormatLogAndAddToFile.
dateTimeNow := Now;
logText.Lines.Add('<' + DateTimeToStr(dateTimeNow) + '> Изтрита е поръчка');
logTextColor.Lines.Add('red');
And this is how I eventually call the procedures. First the procedure to get the formatted log to the richedits
OpenLogInRichEdit(tcxCheckBoxDate.Checked, tcxDate.Date);
And this is the procedure to format the text and give it to the file
LogFileUse.FormatLogAndAddToFile(logText, logTextColor);
Thanks to the comments I've managed to make it work. I've changed the code above. Instead of having 'fcharset0' as a tag, I now have 'fcharset1' and I also changed 'lang9' to 'lang1026' and now I save it properly to the file and it opens perfectly!
If all this scary code is here only to add colored lines to the file, than you should use TRichEdit.SelAttributes with friends: Colorful text in the same line in TRichEdit This way TRichEdit will be able to correctly handle encoding. And if you need some fancy file header or footer, that you do not want to create from code, than you can create empty rtf-file with required header/footer, and use it as a template.
Related
Hello I need a simple function to delete left text strings, see the example below:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := 'Hello test test test [delimitator] goodby.. test teest test';
Delete(S, Pos('[delimitator]', S), MaxInt);
RichEdit1.Text := S;
end;
This function clears all the characters on the right, even if I don't know how many characters there are.
The question is, how to do the reverse, to delete all the characters on the left?
You already know what to use - Pos() and Delete(). Just tweak how you use them:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := 'Hello test test test [delimitator] goodby.. test teest test';
Delete(S, 1, Pos('[delimitator]', S));
RichEdit1.Text := S;
end;
Thanks to everyone's help. This is not very professional, but I came to that conclusion, which can be used separately or together.
procedure TForm1.Button2Click(Sender: TObject);
var
left_delimitator,
right_delimitator: integer;
get_left, get_right,
left_dbg, right_dbg: string;
begin
RichEdit1.Clear;
get_left := 'test 123 [delimitator] all string left deleted.';
get_right := 'all string rigth deleted. [delimitator] test 123';
left_delimitator := Pos('[delimitator]', get_left);
if left_delimitator > 0 then
begin
Delete(get_left, 1, Pos('[delimitator]', get_left));
left_dbg := '[' + get_left;
RichEdit1.Lines.Add( left_dbg );
end;
right_delimitator := Pos('[delimitator]', get_right);
if right_delimitator > 0 then
begin
Delete(get_right, Pos('[delimitator]', get_right), MaxInt);
right_dbg := '[delimitator]' + ' ' + get_right;
RichEdit1.Lines.Add( right_dbg );
end;
end;
You might use the following function to get this done.
Function GetRightPart(InputString, Delimiter : String) : String;
VAR
HelpArr : TArray<String>;
begin
HelpArr := InputString.Split([Delimiter]) ;
Result := HulpArr[1];
end;
I have a DB grid which is sorted (the user clicked a few radio buttons and checkboxes to influence the display).
I would like to export all of the data (not just what is visible in the grid), sorted identically, to CSV - how do I do so? The data - not the user settings, just to clarify.
Thanks in advance for any help
[Update] I build sqlQuery bit by bit, depending on the user's settings of checkboxes & radio groups, then, when one of them changes, I
ActivityADQuery.SQL.Clear();
ActivityADQuery.SQL.Add(sqlQuery);
ActivityADQuery.Open(sqlQuery);
That is to say that there isn't a hard coded query, it varies and I want to export the current settings.
I don't know enough if I want to export from the grid or the dataset (I am just not a db guy, this is my first DBgrid), but I suspect that I want the grid, because it has a subset of fields of he dataset.
I guess that TJvDBGridCSVExport is a Jedi component(?) I have tried to avoid them so far, great as they sound, because I prefer discreet, stand-alone, components to installing a huge collection. That may not be the cleverest thing to do, but it's how I feel - ymmv (and prolly does)
Another solution, works also with (multi)selected rows:
procedure TReportsForm.ExportToCSV(const aGrid : TDBGrid; const FileName : String);
Var
I, J : Integer;
SavePlace : TBookmark;
Table : TStrings;
HeadTable : String;
LineTable : String;
First : Boolean;
Begin
HeadTable := '';
LineTable := '';
Table := TStringList.Create;
First := True;
Try
For I := 0 To Pred(aGrid.Columns.Count) Do
If aGrid.Columns[I].Visible Then
If First Then
Begin
// Use the text from the grid, in case it has been set programatically
// E.g., we prefer to show "Date/time" than "from_unixtime(activity.time_stamp, "%D %b %Y %l:%i:%S")"
// HeadTable := HeadTable + aGrid.Columns[I].FieldName;
HeadTable := HeadTable + ActivityReportStringGrid.Columns[i].Title.Caption + ','; // Previous separated wth semi-colon, not comma! (global)
First := False;
End
Else
begin
// HeadTable := HeadTable + ';' + aGrid.Columns[I].FieldName;
HeadTable := HeadTable + ActivityReportStringGrid.Columns[i].Title.Caption + ',';
end;
Delete(HeadTable, Length(HeadTable), 1); // Remove the superfluous trailing comma
Table.Add(HeadTable);
First := True;
// with selection of rows
If aGrid.SelectedRows.Count > 0 Then
Begin
For i := 0 To aGrid.SelectedRows.Count - 1 Do
Begin
aGrid.DataSource.Dataset.GotoBookmark(pointer(aGrid.SelectedRows.Items[i]));
For j := 0 To aGrid.Columns.Count - 1 Do
If aGrid.Columns[J].Visible Then
If First Then
Begin
lineTable := lineTable + aGrid.Fields[J].AsString;
First := False;
End
Else
lineTable := lineTable + ',' + aGrid.Fields[J].AsString;
Delete(LineTable, Length(LineTable), 1); // Remove the superfluous trailing comma
Table.Add(LineTable);
LineTable := '';
First := True;
End;
End
Else
//no selection
Begin
SavePlace := aGrid.DataSource.Dataset.GetBookmark;
aGrid.DataSource.Dataset.First;
Try
While Not aGrid.DataSource.Dataset.Eof Do
Begin
For I := 0 To aGrid.Columns.Count - 1 Do
If aGrid.Columns[I].Visible Then
If First Then
Begin
lineTable := lineTable + aGrid.Fields[I].AsString;
First := False;
End
Else
lineTable := lineTable + ',' + aGrid.Fields[I].AsString;
Delete(LineTable, Length(LineTable), 1); // Remove the superfluous trailing comma
Table.Add(LineTable);
LineTable := '';
aGrid.DataSource.Dataset.Next;
First := True;
End;
aGrid.DataSource.Dataset.GotoBookmark(SavePlace);
Finally
aGrid.DataSource.Dataset.FreeBookmark(SavePlace);
End;
End;
Table.SaveToFile(FileName);
Finally
Table.Free;
End;
End; // ExportToCSV()
You could use a own tiny procedure wich could be adapted to your needs
Procedure Dataset2SeparatedFile(ads: TDataset; const fn: String; const Separator: String = ';');
var
sl: TStringList;
s: String;
i: Integer;
bm: TBookmark;
Procedure ClipIt;
begin
s := Copy(s, 1, Length(s) - Length(Separator));
sl.Add(s);
s := '';
end;
Function FixIt(const s: String): String;
begin
// maybe changed
Result := StringReplace(StringReplace(StringReplace(s, Separator, '', [rfReplaceAll]), #13, '', [rfReplaceAll]), #10, '', [rfReplaceAll]);
// additional changes could be Quoting Strings
end;
begin
sl := TStringList.Create;
try
s := '';
For i := 0 to ads.FieldCount - 1 do
begin
if ads.Fields[i].Visible then
s := s + FixIt(ads.Fields[i].DisplayLabel) + Separator;
end;
ClipIt;
bm := ads.GetBookmark;
ads.DisableControls;
try
ads.First;
while not ads.Eof do
begin
For i := 0 to ads.FieldCount - 1 do
begin
if ads.Fields[i].Visible then
s := s + FixIt(ads.Fields[i].DisplayText) + Separator;
end;
ClipIt;
ads.Next;
end;
ads.GotoBookmark(bm);
finally
ads.EnableControls;
ads.FreeBookmark(bm);
end;
sl.SaveToFile(fn);
finally
sl.Free;
end;
end;
I have this Delphi function:
function DevuelveResumenEventos(cnnBBDD : TADOConnection;sFecha,sHora,sCtrlPac : string) : TStream;
var
sTextoArmado : string;
stCarga : TStringStream;
begin
with TADOTable.Create(Application.MainForm) do
try
sTextoArmado := '';
Connection := cnnBBDD;
TableName := 'EAPC_EVENTOS';
Filter := 'EAPC_FECHA = '+sFecha+' and EAPC_HORA = '+sHora+' and EAPC_CTRL_PAC = '+sCtrlPac;
Filtered := True;
Open;
while not Eof do
begin
sTextoArmado := sTextoArmado + FormatDateTime('dd-mm-yyyy', FieldValues['EAPC_FECHA_EVENTO'])+
' '+MinutsToStr(FieldValues['EAPC_HORA_EVENTO'])+
' ('+Trim(FieldValues['EAPC_LOGIN_USER'])+
') - '+FieldByName('EAPC_EVENTO').AsString+CRLF+CRLF;
Next;
end;
**stCarga := TStringStream.Create(sTextoArmado);
with TRichEdit.Create(Application.MainForm) do
begin
Parent := Application.MainForm;
Text := sTextoArmado;
Lines.SaveToStream(stCarga);
Free;
end;
finally
Close;
Free;
end;
Result := stCarga;**
end;
The intention is to retrieve a series of RTF formated texts, concatenate them with other texts and return them in a single TStringStream to be displayed in a TRichEdit in a form.
How can I skip the "use the on-the-fly RichEdit" and send the resulting texts as a TStringStream?
I want to export content of a TQuery to a CSV file without using a 3d part component(Delphi 7). From my knowledge this can not be accomplished with Delphi standard components.
My solution was to save the content in a StringList with a CSV format, and save it to a file.
Is there any comfortable solution?
PS:I don't want to use JvCsvDataSet or any component. Question is: can this be accomplished only with Delphi 7 or higher standard components?
Thank you in advance!
Of course it can.
You just have to do the work to properly output the CSV content (quoting properly, handling embedded quotes and commas, etc.). You can easily write the output using TFileStream, and get the data using the TQuery.Fields and TQuery.FieldCount properly.
I'll leave the fancy CSV quoting and special handling to you. This will take care of the easy part:
var
Stream: TFileStream;
i: Integer;
OutLine: string;
sTemp: string;
begin
Stream := TFileStream.Create('C:\Data\YourFile.csv', fmCreate);
try
while not Query1.Eof do
begin
// You'll need to add your special handling here where OutLine is built
OutLine := '';
for i := 0 to Query.FieldCount - 1 do
begin
sTemp := Query.Fields[i].AsString;
// Special handling to sTemp here
OutLine := OutLine + sTemp + ',';
end;
// Remove final unnecessary ','
SetLength(OutLine, Length(OutLine) - 1);
// Write line to file
Stream.Write(OutLine[1], Length(OutLine) * SizeOf(Char));
// Write line ending
Stream.Write(sLineBreak, Length(sLineBreak));
Query1.Next;
end;
finally
Stream.Free; // Saves the file
end;
end;
The original question asked for a solution using a StringList. So it would be something more like this. It will work with any TDataSet, not just a TQuery.
procedure WriteDataSetToCSV(DataSet: TDataSet, FileName: String);
var
List: TStringList;
S: String;
I: Integer;
begin
List := TStringList.Create;
try
DataSet.First;
while not DataSet.Eof do
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do
begin
if S > '' then
S := S + ',';
S := S + '"' + DataSet.Fields[I].AsString + '"';
end;
List.Add(S);
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
List.Free;
end;
end;
You can add options to change the delimiter type or whatever.
This is like the Rob McDonell solution but with some enhancements: header, escape chars, enclosure only when required, and ";" separator.
You can easily disable this enhancements if not required.
procedure SaveToCSV(DataSet: TDataSet; FileName: String);
const
Delimiter: Char = ';'; // In order to be automatically recognized in Microsoft Excel use ";", not ","
Enclosure: Char = '"';
var
List: TStringList;
S: String;
I: Integer;
function EscapeString(s: string): string;
var
i: Integer;
begin
Result := StringReplace(s,Enclosure,Enclosure+Enclosure,[rfReplaceAll]);
if (Pos(Delimiter,s) > 0) OR (Pos(Enclosure,s) > 0) then // Comment this line for enclosure in every fields
Result := Enclosure+Result+Enclosure;
end;
procedure AddHeader;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S + Delimiter;
S := S + EscapeString(DataSet.Fields[I].FieldName);
end;
List.Add(S);
end;
procedure AddRecord;
var
I: Integer;
begin
S := '';
for I := 0 to DataSet.FieldCount - 1 do begin
if S > '' then
S := S + Delimiter;
S := S + EscapeString(DataSet.Fields[I].AsString);
end;
List.Add(S);
end;
begin
List := TStringList.Create;
try
DataSet.DisableControls;
DataSet.First;
AddHeader; // Comment if header not required
while not DataSet.Eof do begin
AddRecord;
DataSet.Next;
end;
finally
List.SaveToFile(FileName);
DataSet.First;
DataSet.EnableControls;
List.Free;
end;
end;
Delphi does not provide any built-in access to .csv data.
However, following the VCL TXMLTransform paradigm, I wrote a TCsvTransform class helper that will translate a .csv structure to /from a TClientDataSet.
As for the initial question that was to export a TQuery to .csv, a simple TDataSetProvider will make the link between TQuery and TClientDataSet.
For more details about TCsvTransform, cf http://didier.cabale.free.fr/delphi.htm#uCsvTransform
I am trying to find out which form and element belongs too. The code that I now understand from this website:
http://www.cryer.co.uk/brian/delphi/twebbrowser/read_write_form_elements.htm
containing this code
function GetFormFieldNames(fromForm: IHTMLFormElement): TStringList;
var
index: integer;
field: IHTMLElement;
input: IHTMLInputElement;
select: IHTMLSelectElement;
text: IHTMLTextAreaElement;
begin
result := TStringList.Create;
for index := 0 to fromForm.length do
begin
field := fromForm.Item(index,'') as IHTMLElement;
if Assigned(field) then
begin
if field.tagName = 'INPUT' then
begin
// Input field.
input := field as IHTMLInputElement;
result.Add(input.name);
end
else if field.tagName = 'SELECT' then
begin
// Select field.
select := field as IHTMLSelectElement;
result.Add(select.name);
end
else if field.tagName = 'TEXTAREA' then
begin
// TextArea field.
text := field as IHTMLTextAreaElement;
result.Add(text.name);
end;
end;
end;
end;
seems to be working fine for most sites. However there are a few websites such as this one:
http://service.mail.com/registration.html#.1258-bluestripe-product1-undef
By looking at that code and comparing it with the active id, I can find the form it is in. However it does not work for that website. for some reason I think it has to do with htmldocument3 adn that this code is for htmldocument2. But I am not sure.
so my question is How can I extract a tstringlist from this website with all the elements names in them? hope you can help!
Edited: Added some code
begin
theForm := GetFormByNumber(webbrowser1.document as IHTMLDocument2,
0);
fields := GetFormFieldNames(theForm);
num := fields.IndexOf(theid);
end;
until (num <> -1);
One complication with locating form elements in a web page is that the page may contain frames and there may be forms in any of the frames. Basically, you have to iterate through all the frames and the forms in each frame. Once you get the form as an IHTMLFormElement, use Cryer's function to get the form element names.
The example link you gave does not have any frames and you should have had no problems getting your list of form elements, unless you tried to get the form by name because it had no name assigned. I had no problem getting the form element names and values using the following procedure
procedure GetForms(doc1: IHTMLDocument2; var sl: TStringList);
var
i, j, n: integer;
docForm: IHTMLFormElement;
slt: TStringList;
s: string;
begin
if doc1 = nil then
begin
ShowMessage('doc1 is empty [GetForms]');
Exit;
end;
slt := TStringList.Create;
n := NumberOfForms(doc1);
sl.Add('Forms: ' + IntToStr(n));
for i := 0 to n - 1 do
begin
docForm := GetFormByNumber(doc1, i);
sl.Add('Form Name: ' + docForm.Name);
slt.Clear;
slt := GetFormFieldNames(docForm);
for j := 0 to slt.Count - 1 do
begin
s := GetFieldValue(docForm, slt[j]);
sl.Add('Field Name: ' + slt[j] + ' value: "' + s + '"');
end;
end;
sl.Add('');
slt.Free;
end;
Cryer's example for navigating a frameset will not work for all web sites, see http://support.microsoft.com/support/kb/articles/Q196/3/40.ASP. The following function successfuly extracts a frame as an IHTMLDocument2 on all sites I have tried
function GetFrameByNumber(Doc:IHTMLDocument2; n:integer):IHTMLDocument2;
var
Container: IOleContainer;
Enumerator: ActiveX.IEnumUnknown;
Unknown: IUnknown;
Browser: IWebBrowser2;
Fetched: Longint;
NewDoc: IHTMLDocument2;
i : integer;
begin
// We cannot use the document's frames collection here, because
// it does not work in every case (i.e. Documents from a foreign domain).
// From: http://support.microsoft.com/support/kb/articles/Q196/3/40.ASP
i := 0;
if (Supports(Doc, IOleContainer, Container)) and
(Container.EnumObjects(OLECONTF_EMBEDDINGS, Enumerator) = S_OK) then
begin
while Enumerator.Next(1, Unknown, #Fetched) = S_OK do
begin
if (Supports(Unknown, IWebBrowser2, Browser)) and
(Supports(Browser.Document, IHTMLDocument2, NewDoc)) then
begin
// Here, NewDoc is an IHTMLDocument2 that you can query for
// all the links, text edits, etc.
if i=n then
begin
Result := NewDoc;
Exit;
end;
i := i+1;
end;
end;
end;
end;
Here is an example of how I have used GetForms and GetFrameByNumber
// from the TForm1 declaration
{ Public declarations }
wdoc: IHTMLDocument2;
procedure TForm1.btnAnalyzeClick(Sender: TObject);
begin
wdoc := WebBrowser.Document as IHTMLDocument2;
GetDoc(wdoc);
end;
procedure TForm1.GetDoc(doc1: IHTMLDocument2);
var
i, n: integer;
doc2: IHTMLDocument2;
frame_dispatch: IDispatch;
frame_win: IHTMLWindow2;
ole_index: olevariant;
sl: TStringList;
begin
if doc1 = nil then
begin
ShowMessage('Web doc is empty');
Exit;
end;
Form2.Memo1.Lines.Clear;
sl := TStringList.Create;
n := doc1.frames.length;
sl.Add('Frames: ' + IntToStr(n));
// check each frame for the data
if n = 0 then
GetForms(doc1, sl)
else
for i := 0 to n - 1 do
begin
sl.Add('--Frame: ' + IntToStr(i));
ole_index := i;
frame_dispatch := doc1.Frames.Item(ole_index);
if frame_dispatch <> nil then
begin
frame_win := frame_dispatch as IHTMLWindow2;
doc2 := frame_win.document;
// sl.Add(doc2.body.outerHTML);
GetForms(doc2,sl);
GetDoc(doc2);
end;
end;
// Form2 just contains a TMemo
Form2.Memo1.Lines.AddStrings(sl);
Form2.Show;
sl.Free;
end;
The logic in your example is faulty, 1. when there is only 1 form on the web page the list of form elements is never extracted, 2. the repeat loop will result in a access violation unless the the tag in "theid" is found
Here is your example cut down to successfully extract the form elements.
var
i : integer;
nforms : integer;
document : IHTMLDocument2;
theForm : IHTMLFormElement;
fields : TStringList;
theform1 : integer;
num : integer;
theid : string;
begin
fields := TStringList.Create;
theid := 'xx';
// original code follows
i := -1;
// nforms := NumberOfForms(webbrowser1.document as IHTMLDocument2);
// document := webbrowser1.document as IHTMLDocument2;
// if nforms = 1 then
// begin
// theForm := GetFormByNumber(webbrowser1.document as IHTMLDocument2, 0);
// theform1 := 0;
// end
// else
begin
// repeat
begin
inc(i);
theForm := GetFormByNumber(webbrowser1.document as IHTMLDocument2,
i);
fields := GetFormFieldNames(theForm);
num := fields.IndexOf(theid);
theform1 := i;
end;
// until (num <> -1);
end;
// end of original code
Memo1.Lines.Text := fields.Text;
fields.Free;
end;
Hm, are you sure this link contains any form elements? At least I did not see any visible ones. Perhaps they are hidden - did not check this myself, however.
Michael