searching a text file the writing result to memo, lazarus - lazarus

this segment in my program first ads a customer to a textfile (declared in public variables) and saves it to a texfile. onbutton1click is the procudere to search the string thats in the editbox and return the relevant customer details to memo. the add customer works fine and adds to textfile, however when i search it returns nothing onto the memo, just the memo caption, memo1. any way i can resolve this? sorry im a newb to this.
procedure TForm2.btnsaveClick(Sender: TObject);
begin
cusfname:= edit1.text ;
cuslname:= edit2.text;
adress:= edit3.text;
phone:= edit4.text;
password:= edit5.Text;
AssignFile(F, 'Data.txt');
append(F);
WriteLn(F, cusfname);
WriteLn(F, cuslname);
WriteLn(F, adress);
WriteLn(F, phone);
WriteLn(F, password);
CloseFile(F);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
SearchFile : Textfile;
found: boolean;
search: string;
begin
search := edit1.text;
Assignfile(SearchFile, 'data.txt');
Reset(SearchFile);
found:= false;
repeat
found:= search = phone
until eof(searchfile) or found;
if found then
memo1.append(phone);
memo1.append(cusfname);
memo1.append(adress);
if not found then
showmessage('member not found');
end;

wonder where are the read statements? In the write function you have Write() statements, but in the reading code no read() statements?

In your code you do not read() from file. In other similar question (probably your own): runerror(102) file not assigned? there is read(). But I think you should use readln(), or even better use TStringList class from Classes unit with its LoadFromFile() method and Lines property.

Related

TShellListView Lazarus

Is it possible to set a item in a ShellListView to visible=false? I thought About something like ShelLlistView.Items.visible(false) but that does not exist and I have no idea for another solution so I hope someone can help me with this.
I can't post any Code for you yet because I don't have any ShellListView yet but I hope you can give me some advice how it could work.
Afaik, this is not possible because the ListItems shown in the TShellListView have no Visible property. However, according to Peter Below (TeamB), you can effectively "hide" an item by destroying it. See http://www.delphigroups.info/2/67/290279.html
Of course, if you wish to "unhide" an item hidden in this way, you would need to create & add a new node with the same Caption, etc.
This code works fine for me using the standard Lazarus TShellListView:
procedure TForm1.Button1Click(Sender: TObject);
var
Item : TListItem;
begin
Item := ShellListView1.Items[0];
Caption := Item.Caption;
Item.Free;
end;
and removes the first item in the list.
The following removes all the items in the ShellListView. THe downto is to account for the fact that the Count value decreases on each iteration of the loop.
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
begin
for i := ShellListView1.Items.Count - 1 downto 0 do
ShellListView1.Items[i].Free;
end;

How to open MSI table in Delphi?

I need to open some tables from MSI database, read this and place some rows in this with using Delphi (in my example is Delphi 7 but allowed other versions if it needed).
For example it would look like ORCA. Msi must be open, written to the table where it's can be edited and written to the msi file.
By default, Delphi can't open MSI tables as I thing but I found a JEDI Windows API where exists libraris like JwaMsi and JwaMsiQuery. But I can't find documentations or examples of using functions like
function MsiOpenProduct(szProduct: LPCTSTR; var hProduct: MSIHANDLE): UINT; stdcall;
{$EXTERNALSYM MsiOpenProduct}
By the way, while I search information about this I found this code:
const msilib = 'msi.dll';
type
MSIHANDLE = DWORD;
TMsiHandle = MSIHANDLE;
function MsiCloseHandle(hAny: MSIHANDLE):UINT;stdcall;external msilib name 'MsiCloseHandle';
function MsiOpenProduct(szProduct:LPCSTR;var hProduct:MSIHANDLE):UINT;stdcall;external msilib name 'MsiOpenProductA';
function MsiGetProductProperty(hProduct:MSIHANDLE;szProperty:LPCSTR;lpValueBuf:LPSTR;pcchValueBuf:LPDWORD):UINT;stdcall; external msilib name 'MsiGetProductPropertyA';
function MsiSetInternalUI(dwUILevel:INSTALLUILEVEL;phWnd:LPHWND):INSTALLUILEVEL;stdcall; external msilib name 'MsiSetInternalUI';
function GetMSIProperty(aProductCode:string):string;
var
msi:TMSIHandle;
t:string;
function _getmsiproperty(_name:string):string;
var
txt:PChar;
sz:DWORD;
begin
sz:=MAX_PATH;
txt:=AllocMem(sz+1);
if MsiGetProductProperty(msi,PChar(_name),txt,#sz)=ERROR_MORE_DATA then
begin
ReAllocMem(txt,sz+1);
MsiGetProductProperty(msi,PChar(_name),txt,#sz);
end;
SetString(Result,txt,sz);
FreeMem(txt,sz+1);
end;
begin
MsiSetInternalUI(2,nil); // скрываем GUI/hide GUI
if MsiOpenProduct(PChar(aProductCode),msi)=ERROR_SUCCESS then
begin
t:=_getmsiproperty('ARPPRODUCTICON'); // главная иконка приложения/main program icon
if t='' then t:=_getmsiproperty('ProductIcon');
if t='' then t:=_getmsiproperty('CompleteSetupIcon');
if t='' then t:=_getmsiproperty('CustomSetupIcon');
if t='' then t:=_getmsiproperty('InfoIcon');
if t='' then t:=_getmsiproperty('InstallerIcon');
if t='' then t:=_getmsiproperty('RemoveIcon');
if t='' then t:=_getmsiproperty('RepairIcon');
Result:=t;
MsiCloseHandle(msi);
end;
end;
What is better to use and where I can see documentation and/or examples?
P.S. Sorry for my English
I would use the COM-based API to MSI.
See this thread and the MSI API documentation
So, solution exist and I do it! It's not so gracefully but it's working...
At first, read articles from MSDN (in my case is articles about work with MSI database).
At second, you must understand what you need to do. In my case it is:
Open msi database
Read table names
Read needed table and show it
If it needed - make changes and save it
Here I show how I do the three first steps.
Preparing
Download JEDI Windows AP (for easer work with msi) and add to project JwaMsi.pas, JwaMsiDefs.pas and JwaMsiQuery.pas (do not forget about USES list). Dependencies will be added automatically.
Next, place on form all needed components.
Let's code!
1 Open msi database and 2 Read table names
Define variables for handlers and for buffers
var msi_handler_DB, msi_handler_view, msi_handler_record:TMSIHandle;
txt:PChar;
sz:DWORD;
Now we need take a list of tables (more info here) and place it in ListBox, for example
begin
sz:=MAX_PATH; //initialise
txt:=AllocMem(sz+1); //variables
OpenDialog1.Execute; //select file
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //check if DB is open
then begin //start reading
Listbox1.Clear; //prepare listbox for filling
MsiDatabaseOpenView(msi_handler_DB, 'SELECT * FROM _Tables',msi_handler_view); //prepare query to _Table
MsiViewExecute(msi_handler_view, msi_handler_record); //execute...
While not MsiViewFetch(msi_handler_view, msi_handler_record)=ERROR_NO_MORE_ITEMS //and fetch it in cycle until end of table
do begin
MsiRecordGetString(msi_handler_record,1,txt,sz); //read string
ListBox1.Items.Add(txt); //and write to listbox
end; //end of fetch cycle
MsiCloseAllHandles; //close handles (we don't need they more)
end; //stop reading
end;
3 Read needed table and show it
It's easy!
begin
edit1.text:=Listbox1.Items.ValueFromIndex[ListBox1.ItemIndex];
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //open database again
then begin
MsiDatabaseExport(msi_handler_DB, pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]), 'C:\Windows\Temp', Pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt')); //export table to .idt
MsiCloseAllHandles; //and close handler again
//...
//here must be placed code for
//parsing .idt as tabulation separated file
//with wordwrap and show it.
//for example - in StringGrid
//...
DeleteFile('C:\Windows\Temp\'+ ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt'); //do not forget delete temporary .idt file. save our planet! :)
end;
end;
4 If it needed - make changes and save it
Just repeat third step from the end to beginning: export StringGrid to file (about .idt and it's structure you can read here: one, two, three), import in to MSI with property MsiDatabaseImport (do not forget open database before and append it after, also remember about close handlers) and delete temporary .idt file.
Good luck!
P.S. Sorry for my English, part two :)

How to pass in argument of type DATE to a function

I've got a function in an oracle database. I need to call it from delphi. I use the following code:
procedure TForm1.Run;
var
q:TADOQuery;
begin
q:=TADOQuery.Create(nil);
q.Connection:=ADOConnection1;
q.ParamCheck:=false;
q.SQL.Add('BEGIN');
q.SQL.Add(' :RES:=Search(:P_DATE);');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftDate;
q.Parameters.ParamByName('P_DATE').Value:=Now;
q.Parameters.AddParameter.Name:='RES';
q.Parameters.ParamByName('RES').DataType:=ftFloat;
q.Parameters.ParamByName('RES').Direction:=pdOutput;
q.Parameters.ParamByName('RES').Value:=1;
q.ExecSQL;
//...
I get ora-06550 error, saying invalid number or type of parameters. If I change the P_DATE parameter to sysdate, i.e. :RES:=Search(sysdate);, it works fine.
So how can I pass an "in" parameter of type DATE to an oracle function from delphi?
Found ORA-06550 when Oracle stored function is called.. However this relates to Kylix Pascal IDE. Am I expected to meet the same behaviour for delphi? Didn't try to use oracle procedure instead of a function. Maybe this issue can be solved some how else...
Oracle doesn't have a date type for bind variables - you need to explicitly convert the bind variable to a date:
:res := search(to_date(:p_date, 'dd/mm/yyyy'));
You should then be able to pass your variable as a string matching the date format you've specified.
Try to send this parameter as a string:
.........
q.SQL.Add('BEGIN');
q.SQL.Add(' :RES:=Search(TO_DATE(:P_DATE,''YYYYMMDD''));');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftString;
q.Parameters.ParamByName('P_DATE').Value:=FormatDateTime('yyyymmdd',Now);
..........
You're doing it wrong (and I think you had a previous question deleted as being a duplicate that asked this same question (different function, but same idea) just a couple of days ago).
You're calling the function wrong in the first place.
.........
q.SQL.Add('BEGIN');
q.SQL.Add(' SELECT Search(TO_DATE(:P_DATE,''YYYYMMDD'')) FROM System.Dual;');
q.SQL.Add('END;');
q.Parameters.AddParameter.Name:='P_DATE';
q.Parameters.ParamByName('P_DATE').Direction:=pdInput;
q.Parameters.ParamByName('P_DATE').DataType:=ftDate;
q.Parameters.ParamByName('P_DATE').Value:=Now;
q.Open;
if not q.IsEmpty then // or not q.Eof
Res := q.Fields[0].AsFloat;

WHMCS API addticketreply not working with message

I have tried everything I can think of and can't get line breaks to go along with the addticketreply command to the API of WHMCS. It's coming from a window's application not PHP or anything. Simple query string, all characters are included before the first line break. After that line break nothing else of the message is included.
I've tried a urlencode method, no go, the API just inserts all text including the encoded part ha ha and doesn't unencode. I've tried replacing line breaks \n with <br> but still WHMCS just shows the <br> ha ha
Any clues how to get full messages through the API including new lines? Documentation is really lacking on this one.
Also the variable adminusername doesn't work at all! I've tried a full name, nope, I've tried username and userid and nope, still inserts my logged in username for ticket replies. The user I am using is a super admin with full access.
The query string for the API looks like this:
accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf
Nothing past that first line goes :(
sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
I should mention I am using Indy for the Post. Everything else works, any other command passed works, heck even the ticket is updated, the message is updated. It just won't go past that first line break.
EDIT CODE: As I mentioned above I DO let IdHTTP encode my text using the post params, so it should work, AND as I also mentioned above I have tried to pre-encode it, with the results being that WHMCS won't un-encode then and just shows the %20's and other's. Since it's been asked I should mention, I am using Indy 10! So I am up to date with the most recent version.
j := TStringList.Create;
ret := TStringStream.Create('');
j.Text := k+params+'action='+method+'&responsetype=json&username='+username1.Text+'&password='+password;
try
htp1.Post('http'+s+'://'+url1.Text+'/includes/api.php', j, ret);
except
on E: EIdHTTPProtocolException do
result := '{"result":"error", "message":"'+htp1.ResponseText+'"}';
end;
Line breaks, spaces, and other reserved characters need to be URL-encoded, eg:
accesskey=key&adminusername=Name%20of%20Person&ticketid=488&message=asdf%20asdf%0D%0ANothing%20past%20that%20first%20line%20goes%20%3A%28%0D%0Asad%20sad%20sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
If you are sending the values via a POST request, then make sure you are usiug an up-to-date version of Indy. TIdHTTP.Post(TStrings) handles the encoding for you, eg:
var
Params: TStringList;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
IdHTTP1.Post(URL, Params);
finally
Params.Free;
end;
end;
However, if you are sending the values via a URL query string, then you have to manually encode the URL, TdHTTP will not handle that for you, eg:
var
URL: String;
begin
URL := 'http://host/path?accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword';
IdHTTP1.Get(TIdURI.URLEncode(URL));
end;
Or:
var
URL: String;
Params: TStringList;
I: Integer;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
for I := 0 to Params.Count-1 do
Params[i] := TIdURI.ParamsEncode(Params[i]);
Params.Delimiter := '&';
Params.StrictDelimiter := True;
Params.Quotechar := #0;
URL := 'http://host/' + TIdURI.PathEncode('path') + '?' + Params.DelimitedText;
IdHTTP1.Get(URL);
finally
Params.Free;
end;
end;

load data from database on date selection

I am trying to load database data by using dbplannercalender1.
procedure TForm1.DBPlannerCalendar1DaySelect(Sender: TObject;
SelDate: TDateTime);
begin
with absQuery2 do
begin
absQuery2.Close;
absQuery2.sql.Clear;
ABSQuery2.SQL.Text:='select * from log where date = :a1';
ABSQUERY2.PARAMS.ParamByName('a1').value:= DBPlannerCalendar1.Date;
ABSQuery2.ExecSQL;
end;
end;
I get the error "date string expected"YYYY-MM-DD",but "="found at line 1..."
What am I doing wrong ?
Use trunced value
adoQuery.Parameters.paramByName('DataDal').value := trunc(edDate.Date);
Use AsDateTime instead of Value, to let the driver convert it to the proper format:
AbsQuery2.Params.ParamByName('a1').AsDateTime := DBPlannerCalendar1.Date;
If Absolute doens't support AsDateTime for some reason, format the date yourself:
AbsQuery2.Params.ParamByName('a1').Value :=
FormatDateTime('yyyy-mm-dd`, DBPlannerCalendar1.Date);
The first method is the best choice, because it works more portably between database engines (it works for all of them, because the driverr does the formatting into the proper arrangement for dates). Using the second means that if the DB expects something different than YYYY-MM-DD, you have to change the code everywhere that uses dates.

Resources