Delphi7 TIniFile Usage - delphi

Am trying to create an ini file to save the an application configuration.
the saving part works perfect with the input from the edit boxes 1,2,3 etc here is the ini sample
[1server]
SSHHost=ssh.com
SSHPort=443
Username=user
Password=123
[2server]
SSHHost=ssh.com
SSHPort=443
Username=user
Password=123
[ProxySettings]
Proxy=127.0.0.1
Port=8080
Type=http
how do i make the app read the saved ini setting on launching it and is it possible to hide or encrypt user saved password?

Simply read the Ini file in your main form's FormCreate event.
procedure TForm1.FormCreate(Sender: TObject);
var
Ini: TIniFile;
begin
Ini := TIniFile.Create(YourIniFileName);
try
// The final parameter to all of the `ReadXx` functions is a default
// value to use if the value doesn't exist.
Edit1.Text := Ini.ReadString('1server', 'SSHHost', 'No host found');
Edit2.Text := Ini.ReadString('1server', 'SSHPort', 'No port found');
// Repeat, using ReadString, ReadInteger, ReadBoolean, etc.
finally
Ini.Free;
end;
end;
One word of caution: TIniFile has known issues with writing to network locations, so if there's any chance of that you should use TMemIniFile instead. TIniFile wraps the WinAPI INI support functions (ReadPrivateProfileString and others), while TMemIniFile is written totally in Delphi code and doesn't suffer from the same problems. They're syntax-compatible and in the same unit, so it's a simple matter of changing TIniFile to TMemIniFile in your variable declaration and the line that creates the Ini:
var
Ini: TMemIniFile;
begin
Ini := TMemIniFile.Create(YourIniFileName);
...
end;
As far as encrypting the password, you can use any encryption algorithm you want, as long as the encrypted value can be converted to a textual representation. (Ini files don't handle binary values.) The choice of algorithm is up to you based on the level of security you're attempting to achieve.

Related

How to read a TBlobfield from a query [duplicate]

I have problem with reading blob field from database which contains msword file and save it into file (.doc/.docx). What is moree this works great in Delphi 2010 but in Delphi Xe2 saved files are invalid.This is my code
dane.SQLtmp.Close;
dane.SQLtmp.SQL.Clear;
dane.SQLtmp.SQL.Add('select wydruk,typ,IdWydruku from wydruki where nazwa=:d0');
dane.SQLtmp.Params[0].AsString:=name;
dane.SQLtmp.Open;
if dane.SQLtmp.RecordCount> 0 then
begin
t:=TMemoryStream.Create;
t.Position:=0;
TblobField(dane.sqltmp.FieldByName('wydruk')).saveToStream(T);
T.SaveToFile('C:\FILE'+filetpe);
t.Free;
end;
Saving file into database:
dane.SQLtmp.Close;
dane.SQLtmp.SQL.Clear;
dane.SQLtmp.SQL.Add('insert into Wydruki (Nazwa,Operator,wydruk,opis,typ,rodzaj,podmiot,typsplaty,grupa,podgrupa)');
dane.Sqltmp.SQL.Add('VALUES (:d0,:d1,:d2,:d3,:d4,:d5,:d6,:d7,:d8,:d9)');
dane.SQLtmp.Params[0].AsString:=NazwaPliku; //File name
dane.SQLtmp.Params[1].AsInteger:=glowny.ID_operator;
t:=TMemoryStream.Create;
t.Position:=0;
t.LoadFromFile(OpenFile.FileName);
t.Position:=0;
dane.sqltmp.Params[2].LoadFromStream(t,ftBlob);
dane.SQLtmp.Params[3].AsString:=opis;
dane.SQLtmp.Params[4].AsString:=typ; // file type
// .
// .
// .
dane.SQLtmp.ExecSQL;
In Delphi 2010 it worked... :/
You need to use TBlobField.CreateBlobStream and copy to a TFileStream.
According to the documentation:
Call CreateBlobStream to obtain a stream for reading and writing the value of the field specified by the Field parameter. The Mode parameter indicates whether the stream will be used for reading the field's value (bmRead), writing the field's value (bmWrite), or modifying the field's value (bmReadWrite).
The Tip on the same documentation page says:
Tip: It is preferable to call CreateBlobStream rather than creating a blob stream directly in code. This ensures that the stream is appropriate to the dataset, and may also ensure that datasets that do not always store BLOB data in memory fetch the blob data before creating the stream.
Sample code based on yours above:
var
Blob: TStream;
Strm: TFileStream;
BlobFld: TBlobField;
begin
dane.SQLtmp.SQL.Text := 'select wydruk,typ,IdWydruku from wydruki where nazwa=:d0';
dane.SQLtmp.Params[0].AsString:=name;
dane.SQLtmp.Open;
BlobFld := dane.SQLtmp.FieldByName('wydruk') as TBlobField;
Blob := dane.SQLtmp.CreateBlobStream(BlobFld, bmRead);
try
Strm := TFileStream.Create('C:\FILE' + filetpe, fmCreate);
try
Strm.CopyFrom(Blob, Blob.Size);
finally
Strm.Free;
end;
finally
Blob.Free;
end;
end;
The problem was in Interbase components (IB) in Xe2. When I changed them to FireDac'c components my and Yours code works.
That's interesting, recently embarcadero's product have many bugs.
You can do this
query.SQL.Add('SELECT * FROM something');
query.Open;
query.FieldByName('file') as TBlobField).SaveToFile(SaveDialog1.FileName);

The best solution to process the huge text file data in Delphi 7

I have text file like this:
"01","AAA","AAAAA"
"02","BBB","BBBBB","BBBBBBBB"
"03","CCC"
"04","DDD","DDDDD"
I want to load this text file data into temp table in sybase db. So, I need to build a program to read line by line this text file until eof. If the text file size is small, the process to read line by line is fast. But if text file size is too big (can be more than 500M), the process read line by line is too slow. I think the read line by line method not suitable for huge text file. So, need to find other solution to load text file data into db instead of read text file line by line method. Any suggestion?
Example code:
var
myFile : TextFile;
text : string;
begin
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
TempTable.append;
TempTable.FieldByName('Field1').asstring=Copy(text,2,2);
TempTable.FieldByName('Field2').asstring=Copy(text,7,3);
TempTable.FieldByName('Field3').asstring=Copy(text,13,5);
TempTable.FieldByName('Field4').asstring=Copy(text,21,8);
TempTable.post;
end;
// Close the file for the last time
CloseFile(myFile);
end;
Some general tips:
Ensure your TempTable is in memory, or use a fast database engine - take a look at SQlite3 or other means (like FireBird embedded, NexusDB or ElevateDB) as possible database alternatives;
If you do not use a TTable, but a true database, ensure you nest the insert within a Transaction;
For a true database, check out if you can not use ArrayDML feature, which is much faster for inserting a lot of data as you want in a remote database (like Sybase) - such Array DML is handled for instance with FireDAC AFAIK;
The FieldByName('...') method is known to be very slow: use locals TField variables instead;
When using a TextFile, assign a bigger temporary buffer;
If you are using newer Unicode versions of Delphi (2009+), using TextFile is not the best option.
So your code may be:
var
myFile : TextFile;
myFileBuffer: array[word] of byte;
text : string;
Field1, Field2, Field3, Field4: TField;
begin
// Set Field* local variables for speed within the main loop
Field1 := TempTable.FieldByName('Field1');
Field2 := TempTable.FieldByName('Field2');
Field3 := TempTable.FieldByName('Field3');
Field4 := TempTable.FieldByName('Field4');
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
SetTextBuf(myFile, myFileBuffer); // use 64 KB read buffer
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
TempTable.append;
Field1.asInteger := StrToInt(Copy(text,2,2));
Field2.asString := Copy(text,7,3);
Field3.asString := Copy(text,13,5);
Field4.asString := Copy(text,21,8);
TempTable.post;
end;
// Close the file for the last time
CloseFile(myFile);
end;
You can achieve very high speed with embedded engines, with almost no size limit, but your storage. See for instance how fast we can add content to a SQLite3 database in our ORM: about 130,000 / 150,000 rows per second in a database file, including all ORM marshalling. I also found out that SQLite3 generates much smaller database files than alternatives. If you want fast retrieval of any field, do not forget to define INDEXes in your database, if possible after the insertion of row data (for better speed). For SQLite3, there is already an ID/RowID integer primary key available, which maps your first data field, I suppose. This ID/RowID integer primary key is already indexed by SQLite3. By the way, our ORM now supports FireDAC / AnyDAC and its advanced Array DML feature.
Text files normally have a very small buffer. Look into using the SetTextBuf function to increase your performance.
var
myFile : TextFile;
text : string;
myFileBuffer: Array[1..32768] of byte;
begin
// Try to open the Test.txt file for writing to
AssignFile(myFile, 'Test.txt');
SetTextBuf(MyFile, myFileBuffer);
Reset(MyFile);
// Display the file contents
while not Eof(myFile) do
begin
ReadLn(myFile, text);
end;
// Close the file for the last time
CloseFile(myFile);
end;
In addition to what has already been said, I would also avoid using any TTable component. You would be better off using a TQuery type component (depending on the access layer you're using). Something like this :-
qryImport.SQL := 'Insert Into MyTable Values (:Field1, :Field2, :Field3, :Field4);';
Procedure ImportRecord(Const pField1, pField2, pField3, pField4 : String);
Begin
qryImport.Close;
qryImport.Params[0].AsString := pField1;
qryImport.Params[1].AsString := pField2;`
qryImport.Params[2].AsString := pField3;
qryImport.Params[3].AsString := pField4;
qryImport.ExecSQL;
End;
Hope this helps.
Another approach would be to use memory-mapped files (you can google or go torry.net to find implementations). it would not work well for files >1gb (in win32,, in win64 you can map virtually any file). It would turn all your file into PAnsiChar that you would be able to scan like a one large buffer, searching for #10 and #13 (alone or in pairs) and thus manually splitting strings.
If you use (or don't mind starting to use) the JEDI Jvcl, they have a TJvCSVDataSet which allows you to simply use your CSV file like any other dataset in Delphi, including being able to define persistent fields and use "standard" Delphi database functionality:
JvCSVDataSet1.FileName := 'MyFile.csv';
JvCSVDataSet1.Open;
while not JvCSVDataSet1.Eof do
begin
TempTable.Append; // Posts last appended row automatically;
// no need to call Post here.
// Assumes TempTable has same # of fields in the
// same order
for i := 0 to JvCSVDataSet1.FieldCount - 1 do
TempTable.Fields[i].AsString := JvCSVDataSet1.Fields[i].AsString;
JvCSVDataSet1.Next;
end;
// Post the last row appended when the loop above exited
if TempTable.State in dsEditModes then
TempTable.Post;
In Delphi 7 you can use Turbo Power SysTools TStAnsiTextStream() to read and write in a line oriented way, but using the thread safe TStream implementation and not the unsafe old pascal file interface. In later Delphi versions you will find something alike in the standard RTL (although they are a little different in their implementation), but Delphi 7 didn't offer much for text file manipulation.

How to create your own non system Clipboard?

Is it possible, and if so how would you go about implementing your own clipboard?
By this I mean be able to Copy and Paste anything to and from it just like the Windows clipboard does, but without actually interfering with the system clipboard.
To give a better idea this is what I tried:
uses
ClipBrd;
...
procedure TMainForm.actCopyExecute(Sender: TObject);
var
MyClipboard: TClipboard;
begin
MyClipboard := TClipboard.Create;
try
MyClipboard.AsText := 'Copy this text';
finally
MyClipboard.Free;
end;
end;
That works in that it will copy the string "Copy this text" to the clipboard, but it overwrites whatever was on the Windows clipboard.
The above must just create an instance of the Windows clipboard, not actually creating your own.
Note that the custom clipboard could hold any data not just plain text. It should work just the same as the Windows clipboard, but without interfering with it (losing whatever was on it).
How could this be achieved?
Thanks.
Your question is confusing; you say you want to do it without affecting the system clipboard, but then (from your own comment to your question) you seem to be wanting to implement something like MS Office's Paste Special.
If it's the first, as others have said you can't do that using the TClipboard wrapper; you have to implement your own, and passing information between applications will be very difficult.
If it's the second, you do this by using the Windows API RegisterClipboardFormat to define your own format.
type
TForm1=class(TForm)
YourCustomFormat: Word;
procedure FormCreate(Sender: TObject);
end;
implementation
constructor TForm1.FormCreate(Sender: TObject);
begin
YourCustomFormat := RegisterClipboardFormat('Your Custom Format Name');
end;
To put info into the clipboard in a custom format, you have to use GlobalAlloc and GlobalLock to allocate and lock a global memory block, copy your data into that block, unlock the block using GlobalUnlock, use TClipboard.SetAsHandle to transfer the memory block into the clipboard. You then need to then call GlobalFree to free the memory block.
To retrieve things in your custom format, you do basically the same thing with a couple of steps reversed. You use GlobalAlloc/GlobalLock as before, use TClipboard.GetAsHandle to retrieve the clipboard's content, copy it into a local variable, and then call GlobalFree.
Here's an old example of putting a custom format (in this case, RTF text) into the clipboard - it's from a newsgroup post by Dr. Peter Below of TeamB. (The code and formatting are his from the original post; I've not tested it or even compiled it.) Reversing the process to get it back out should be clear from my instructions on what to change above, and I leave that to you to work out. :)
procedure TForm1.BtnSetRTFClick(Sender: TObject);
Const
testtext: PChar = '{\rtf1\ansi\pard\plain 12{\ul 44444}}';
testtext2: PChar = '{\rtf1\ansi'+
'\deff4\deflang1033{\fonttbl{\f4\froman\fcharset0\fprq2 Times New Roman;}}'
+'\pard\plain 12{\ul 44444}}';
flap: Boolean = False;
Var
MemHandle: THandle;
rtfstring: PChar;
begin
If flap Then
rtfstring := testtext2
Else
rtfstring := testtext;
flap := not flap;
MemHandle := GlobalAlloc( GHND or GMEM_SHARE, StrLen(rtfstring)+1 );
If MemHandle <> 0 Then Begin
try
StrCopy( GlobalLock( MemHandle ), rtfstring );
GlobalUnlock( MemHandle );
With Clipboard Do Begin
Open;
try
AsText := '1244444';
SetAsHandle( CF_RTF, MemHandle );
finally
Close;
end;
End;
Finally
GlobalFree( MemHandle );
End;
End
Else
MessageDlg('Global Alloc failed!',
mtError, [mbOK], 0 );
end;
You should define your own custom Clipboard. It may look something like this:
type
TMyCustomClipboard = class
private
FStream: TMemoryStream;
function GetAsText: string;
procedure SetAsText(const Value: string);
...
public
constructor Create;
destructor Destroy; override;
procedure Clear;
property AsText: string read GetAsText write SetAsText;
procedure AsAnyThing: AnyType read GetAsAnyThing write AsAnyThing;
...
end;
Then you can use FStream as custom clipboard container. You can store (Copy) any data inside that stream and use(Paste) it when you need it. You just need to write some Get/Set methods for your data types.
TClipboard is a class incapsulating system clipboard, so you can't use it to instantiate another copy of a clipboard. You should implement your own class, representing a universal buffer with setters and getters.
You cannot. You can have an internal memory buffer that you move data into and out of, you can call it "copy" and "paste" if you want, but don't put it in the user interface that way, or you'll just confuse your users. There is only one system clipboard, and you cannot put data in it without affecting other programs. If your next thought is to save the clipboard, overwrite with your stuff, then restore the original contents, don't bother.

How operate on TFileStream

Hello recently I replace TextFile with TFileStream. I never use it so I have small problem with it.
How can I add someting to my file after I assign it to variable?
How can I read someting form that file?
I need defined line form that file so I was doing something like that:
var linia_klienta:array[0..30] of string;
AssignFile(tempPlik,'klienci.txt');
Reset(tempPlik);
i:=0;
While Not Eof(tempPlik) do
begin
Readln(tempPlik,linia_klient[i]);
inc(i);
end;
CloseFile(tempPlik);
Then when line two is needed I simply
edit1.text = linia_klienta[1];
If you need to read a text file and access each line, try instead using a TStringList class with this class you can load a file, read the data (accesing each line using a index) and save the data back.
something like this
FText : TStringList;
i : integer;
begin
FText := TStringList.Create;
try
FText.LoadFromFile('C:\Foo\Foo.txt');
//read the lines
for i:=0 to FText.Count-1 do
ProcessLine(FText[i]); //do something
//Add additional lines
FText.Add('Adding a new line to the end');
FText.Add('Adding a new line to the end');
//Save the data back
FText.SaveToFile('C:\Foo\Foo.txt');
finally
FText.Free;
end;
end;
end;
I newer versions of Delphi you can use TStreamReader / TStreamWriter here is an example of using TStreamReader ... this is only for manipulating text files
var
SR : TStreamReader;
line : String;
begin
SR := TStreamReader.Create('D:\test.txt');
while not (SR.EndOfStream) do
begin
line := SR.ReadLine;
ShowMessage(line);
end;
SR.Free;
end;
TStream and its immediate descendants are mostly low-level access class. They mostly deal with generic buffers. There are some more specialized classes that descend from or use a stream to perform higher level tasks.
Since Delphi 1 TReader and TWriter could be used to read and write Delphi types directly (inlcuding strings), but they were not designed to handle "line-oriented" files (unluckily they were designed too much with component properties streaming in mind, not as a general purpose framework).
Turbo Power SysTools has a nice TStAnsiTextStream class that implements line-oriented access to text files in a way similar to that of TextFile. Since Delphi 2009 new classes (see opc0de answer) implement the same kind of access without the need of third party libraries (moreover they support different encodings thanks to Delphi 2009 extend codepage support, including Unicode).
Depending with what you want to do, its the stream class you need.
Do you want to work with text (characters with break-lines and end-of-line characters) data ?
OR, do you want to work with binary data ?
I see you are using an array of char, instead, of a string.
Do you really want to use character data as if it was binary ?
Sometimes, some applications require that case.

Create and/or Write to a file

I feel like this should be easy, but google is totally failing me at the moment. I want to open a file, or create it if it doesn't exist, and write to it.
The following
AssignFile(logFile, 'Test.txt');
Append(logFile);
throws an error on the second line when the file doesn't exist yet, which I assume is expected. But I'm really failing at finding out how to a) test if the file exists and b) create it when needed.
FYI, working in Delphi XE.
You can use the FileExists function and then use Append if exist or Rewrite if not.
AssignFile(logFile, 'Test.txt');
if FileExists('test.txt') then
Append(logFile)
else
Rewrite(logFile);
//do your stuff
CloseFile(logFile);
Any solution that uses FileExists to choose how to open the file has a race condition. If the file's existence changes between the time you test it and the time you attempt to open the file, your program will fail. Delphi doesn't provide any way to solve that problem with its native file I/O routines.
If your Delphi version is new enough to offer it, you can use the TFile.Open with the fmOpenOrCreate open mode, which does exactly what you want; it returns a TFileStream.
Otherwise, you can use the Windows API function CreateFile to open your file instead. Set the dwCreationDisposition parameter to OPEN_ALWAYS, which tells it to create the file if it doesn't already exist.
You should be using TFileStream instead. Here's a sample that will create a file if it doesn't exist, or write to it if it does:
var
FS: TFileStream;
sOut: string;
i: Integer;
Flags: Word;
begin
Flags := fmOpenReadWrite;
if not FileExists('D:\Temp\Junkfile.txt') then
Flags := Flags or fmCreate;
FS := TFileStream.Create('D:\Temp\Junkfile.txt', Flags);
try
FS.Position := FS.Size; // Will be 0 if file created, end of text if not
sOut := 'This is test line %d'#13#10;
for i := 1 to 10 do
begin
sOut := Format(sOut, [i]);
FS.Write(sOut[1], Length(sOut) * SizeOf(Char));
end;
finally
FS.Free;
end;
end;
If you are just doing something simple, the IOUtils Unit is a lot easier. It has a lot of utilities for writing to files.
e.g.
procedure WriteAllText(const Path: string; const Contents: string);
overload; static;
Creates a new file, writes the specified string to the file, and then
closes the file. If the target file already exists, it is overwritten.
You can also use the load/save feature in a TStringList to solve your problem.
This might be a bad solution, because the whole file will be loaded into memory, modified in memory and then saved to back to disk. (As opposed to your solution where you just write directly to the file). It's obviously a bad solution for multiuser situations.
But this approach is OK for smaller files, and it is easy to work with and easy understand.
const
FileName = 'test.txt';
var
strList: TStringList;
begin
strList := TStringList.Create;
try
if FileExists(FileName) then
strList.LoadFromFile(FileName);
strList.Add('My new line');
strList.SaveToFile(FileName);
finally
strList.Free;
end;
end;

Resources