Delphi Outlook Contact Automation - Iterate thru ALL properties of a ContactItem - delphi

I am currently writing a small program to extract ALL data elements of my Outlook contacts to a new CRM database. I have imported the necessary type library and came up with the following code (It's almost there!!!)
`
Contacts := NameSpace.GetDefaultFolder(olFolderContacts);
for i := 1 to Contacts.Items.Count do
begin
Contact := Contacts.Items.Item(i);
{now you can read any property of contact. For example, full name and
email address}
// ShowMessage(Contact.FullName + ' <' + Contact.Email1Address + '>');
Memo1.Lines.Add(Contact.FullName + ' <' + Contact.Email1Address + '>');
Memo1.Lines.Add(IntToStr(Contact.ItemProperties.Count) + '>');
for j := 0 to Contact.ItemProperties.Count -1 do
if Contact.ItemProperties.Item(j) <> null then
Memo1.Lines.Add(Contact.ItemProperties.Item(j));
end;
Iterating thru the contacts works OK (I see them in the Memo). My problem is the 2nd "for" loop in trying to check ALL 168 fields of the contact (the 168 shows in the memo too)
Can anyone help shed some light on iterating thru each Property of each contact item? I have found some answers but I need/want to drill down further.
Thanks in advance
Bill

Your comparison to null in that way might be the problem. Try:
for j := 0 to Contact.ItemProperties.Count -1 do
Memo1.Lines.Add(Contact.ItemProperties.Item(j).Name);
or even:
for j := 0 to Contact.ItemProperties.Count -1 do
if Contact.ItemProperties.Item(j).Name <> null then
Memo1.Lines.Add(Contact.ItemProperties.Item(j).Name);
I am not sure but you can even try to access the items in ItemProperties like:
Contact.ItemProperties[j].Name

OK, after spending some quality time in the TLB for office I was able to walk the properties of each contact whether useful or not. The RTFBody property had to be converted from Unicode to ANSI WideString, the attachments as a collection (count was there so easy enough to iterate), and dispatch values I just bypassed. Learned a lot, MSDN Office Interop online docs provided good help in handling field types. The long handed dot notation was necessary because I could not seem to get grouping to work but stepping down each level worked just fine. The important parts follow. As always thanks for the input.
Memo1.Lines.Add(IntToStr(Contacts.Items.Count) + '>');
for i := 1 to Contacts.Items.Count do
begin
Contact := Contacts.Items.Item(i);
{now you can read any property of contact. For example, full name and
email address}
for j := 0 to Contact.ItemProperties.Count -1 do
begin
ContactProperty := Contact.ItemProperties.Item(j);
if not VarIsNull(ContactProperty.Value) then
if ContactProperty.Name = 'RTFBody' then
begin
RTFByte := ContactProperty.Value;
SetString(tANSIStr, PAnsiChar(#RTFByte[0]), Length (ContactProperty.Value));
Memo1.Lines.Add(ContactProperty.Name + ' <' + tANSIstr + '>');
RichEdit1.Lines.Add(tANSIstr);
end
else
if ContactProperty.Name = 'Attachments' then
begin
Attachments := Contact.Attachments;
for k := 1 to Attachments.Count do
begin
ContactAttachment := Attachments.Item(k);
Memo1.Lines.Add('Attachment.FileName <' + ContactAttachment.FileName + '>');
end;
end
end;

Related

Delphi: Help needed with errors with strings and integers

I need to make a 'Calculator' that works out and average for 3 different marks.
procedure TForm1.btnAddClick(Sender: TObject);
var
sName : string;
iprac, itheory, iproject : integer;
begin
sName := edtStudentName.text;
iprac := sedPrac.value;
iTheory := sedTheory.Value;
iProject := sedProject.Value;
lblOutput.caption := sName + ' You got ' + IntToStr (iPrac + iTheory + iProject) / 3;
end;
end.
Thats the code so far, but I get an error:
[Error] ComputerStudyMarks_u.pas(46): Incompatible types: 'String' and 'Integer'
Please be patient with me! I am a hardcore noob but this is for school.
Maybe someone can help me with this program.
I am busy using the book "Enjoy Delphi" and the code I am copying from the book itself is giving me an error.
Any help would be much appreciated!
P.S I cant add a picture of the form because I dont have enough reputation :(
iprac := sedPrac.value;
It's not clear what sedPrac is. And therefore what the Value property it. If Value is a string then you cannot assign a string to an integer. Your would need to convert to integer first, like this:
iprac := StrToInt(sedPrac.value);
Likewise for iTheory and iProject.
Of course, it may be that Value is already an integer, in which case your code is fine. My best guess is that these variables refer to TSpinEdit controls, and that Value is an integer, and so all is well. But that is just a guess.
IntToStr (iPrac + iTheory + iProject) / 3
Now, IntToStr is a function that returns a string. And there is no / operator defined for strings. Presumably you meant to write:
IntToStr((iPrac + iTheory + iProject) div 3)
Note that you have to use div since the value is an integer and / is floating point division.
Some final pieces of advice.
When presenting code, present all the information. You omitted to present some types here and so we don't know what sedPrac is.
When you present an error message, make sure that we can marry up the line number in the message with the code that you present. In your question, we don't know which line is line 46.

How Can I Create Insert and Update SQL's by pure code

I'm currently using AdoQuery's and append post commands. But for data security I want to change my code with insert into and update table name...
But I have a lot of forms and tables...
Because of that I think maybe someone has already developed code for generating insert statements.
Actually I have found a way but I'm stuck.
I have query1. it contains the fieldlist.
I'm creating a parameter list in another query from this fieldlist.
I'm updating the parameters field by field.
This is not very convenient
Can someone give me a easy ways to do this.
Note: I prefer coding this job with only standard components. I don't want to install additional components.
Maybe not the reply you want. I think you need to raise the abstraction level. You need to skip SQL. An ORM framework can do this for you. It maybe feels like a big step for you but I promise it is also a relief to just use code like:
Person.name := 'Bob';
Invoice.customer.address.street := 'Abbey road';
Edit1.text := Invoice.customer.name;
To actually update database you need to call an update method that differ depending on framework. For a list of frameworks see here. I am also aware of TMS Aurelius. I use Bold on daily use. Bold also have features like OCL, derived attributes and links in the model, some boldaware components (it updates whenever db changes). But it has one big disadvantage. It is only available for D2006/D2007. I am working for a solution on this because I think it is the best and most mature ORM framework for Delphi. See also my blog on Bold for Delphi. Ask if you have questions!
You take the fieldlist from your query.
Create a new query with parameters.
And fill in the values.
Something like this:
const
TableNameEscapeStart = '['; //SQL server, use '`' for MySQL
TableNameEscapeEnd = ']'; //SQL server, use '`' for MySQL
FieldNameEscapeStart = '[';
FieldNameEscapeEnd = ']';
function CreateInsertStatementFromTable1ToTable2(Table1, Table2: TTable): String;
var
i: integer;
comma: string;
begin
i:= 0;
Result:= 'INSERT INTO '+TableNameEscapeStart + Table2.TableName + TableNameEscapeEnd + ' (';
comma:= ' , '
while i < Table1.FieldCount do begin
if (i = Table1.FieldCount -1) then begin comma:= ' '; end;
Result:= Result + FieldNameEscapeStart + Table1.Fields.Field[i].Name + FieldNameEscapeEnd + comma;
end;
Result:= Result +' ) VALUES ( ';
i:= 0;
comma:= ' , '
while i < Table1.FieldCount do begin
if (i = Table1.FieldCount -1) then begin comma:= ' '; end;
Result:= Result +':' + IntToStr(i+1) + comma;
end; {while}
Result:= Result + ' ); ';
end;
There are three avenues for SQL injection here.
1. The field values
2. The table name
3. The field names
The first is covered by the use of parameters.
The second and third are covered, because you're using the table and field names of the table directly.
If you don't have a trusted source of table and fields names, then you need to compare these against the table and fieldnames obtained directly from the table.
See: Delphi - prevent against SQL injection
You insert the data using ParamByName (slowly) or more efficiently using Param[i] where i starts at 0.
In MySQL it's even easier:
If table1 and table2 have the same fields, the following SQL will insert all data in table2 into table1:
INSERT INTO table1 SELECT * FROM table2;

Showing accumulated messages to the user

I want to show the user a summary of all the relevant messages that have occurred during a code execution (e.g. parsing, algorithm, conversion, validation etc). The messages should be showed together when the process is done.
A similar incident might occur none, one or several times. The user should be notified if the incident occurred. There might be several types of incidents.
I'm not sure if the scenario is clear, but maybe some code will help:
PSEUDO-CODE:
begin
//Execute computing process//
repeat
Set a flag if an incident occurs
Set another flag if another incident occurs
until done
//Show message to user//
if AnyFlagIsSet then
ShowPrettyMessageToUser
end;
EXECUTABLE DELPHI CODE:
program Test;
{$APPTYPE CONSOLE}
uses
SysUtils, StrUtils;
var
i: Integer;
tmpFlags: Array[1..4] of Boolean;
tmpMessage: String;
tmpChar: Char;
begin
Randomize;
repeat
//Initialization//
for i := 1 to 4 do
tmpFlags[i] := False;
//Will insident occur?//
for i := 0 to 5 do
begin
if (Random(10) = 0) then tmpFlags[1] := True;
if (Random(10) = 0) then tmpFlags[2] := True;
if (Random(10) = 0) then tmpFlags[3] := True;
if (Random(10) = 0) then tmpFlags[4] := True;
end;
//Show message//
tmpMessage := '';
if tmpFlags[1] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 1';
if tmpFlags[2] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 2';
if tmpFlags[3] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 3';
if tmpFlags[4] then tmpMessage := tmpMessage + IfThen(tmpMessage <> '', #13#10+#13#10) + 'Incident 4';
Writeln('----------');
Writeln(tmpMessage);
Writeln('----------');
Writeln;
Write('Again? (Y/N) ');
Readln(tmpChar);
until tmpChar <> 'y';
end.
The code in the iteration is quiet complex in real life, of course.
And the messages are also more informative, and may even be formatted and multi-lined.
So...
Is there a best practice or pattern that can be used for this?
Any Delphi-component that handles this?
An easy solution would be to use a TStringList to collect all messages. You can then either display the strings in a listbox or concatenate the strings (in this case all messages should be valid sentences).
Pseudocode:
procedure DoSomething(Log : TStrings);
begin
//...
Log.Add ('Some hint.');
//...
Log.Add ('Some error happened.');
//...
end;
DoSomething (Log);
if (Log.Count > 0) then
LogListBox.Items.AddStrings (Log);
For formatted or multi-lined messages you could store HTML strings in the stringlist and use a component that can display HTML formatted text to display the messages.
Edit: If you want no duplicate messages, just do
Log.Duplicates := dupIgnore;
I would (where the memory requirements are not a factor) create a class which uses a hashed stringlist as a catalog, where X number of "logitems" could be inserted as objects. This allows some control over how the items are grouped. By accessing the stringlist using a normal index - you get a linear timeline. But accessing the items through it's hash-keys, you get the items grouped by content. Using a hashed stringlist is much faster to work with since it employes a lookup-table.
For an overall, application-wide solution, I have used the midas library's TClientDataset which has been with Delphi for ages. Just remember to declare "midaslib" as the first unit in your project and it's linked directly into your binary (no need to ship midaslib.dll). This gives you the benefit of fields and sorting by message/error types. As long as the record count dont get above 10k it's both fast and stable to work with.

Work With checked listBox

hello
i want implement a algorithm that uses checked listbox
what i need is uses checked items data and index from begin
how can I do it?
I am not sure whether you want this or not.
You must use CheckListBox1.Checked[i] function
This code will return the checked items in the TCheckListBox along with its index.
for I := 0 to CheckListBox1.Items.Count - 1 do
begin
if CheckListBox1.Checked[i] then
begin
ShowMessage('Item at index ' + IntToStr(i) + ' is selected.' +
'Its value is '+ CheckListBox1.Items.Strings[i]);
end;
end;

How to add up the integer values in a label in delphi

I am currently having problems with screating a scoreboard in Delphi.
I have a series of forms which are individual questions.
If the questions are answered correctly, then the score is 1. Otherwise the score is -1.
On my scoreboard at the moment, I have 12 labels and 11 of them contain the score for each of the forms.
What I would like to do is add up the numbers in each of the labels and output the final score into the 12th label.
Is there a way of doing this?
Any help will be greatly appreciated.
You should use the UI purely for displaying your values.
For working with your data you should use appropriate data structures: array, lists, etc.
Example using an Array:
var
Scores[0..10]: Integer;
Sum: Integer;
procedure CollectData;
var
i: Integer;
begin
Scores[0] := ...;
//...
Scores[10] := ...;
Sum := 0;
for i := Low(Scores) to High(Scores) do
Sum := Sum + Scores[i];
end;
procedure DisplayData;
begin
Label1.Caption := IntToStr(Scores[0]);
//...
Label11.Caption := IntToStr(Scores[10]);
Label12.Caption := IntToStr(Sum);
end;
Neat solution: Keep scores as integers in Integer fields
Not so neat solution:
SumLabel.Caption := IntToStr( StrToIntDef( Label1.Caption, 0 ) + StrToIntDef( Label2.Caption, 0 ) + ... );
Although I think #DR's answer is spot-on, and #Ritsaert's is helpful, here's another option.
Your label components will have a 'TAG' property - you can use this for your own purposes and in your case I'd just set the TAG property at the same time as you set the Caption.
The advantage behind this is that you can format your caption to contain more than a simple number (if you wish), and also you are just summing up tags (which are already integers and don't need you to do the extra work behind a StrToIntDef call). Really, you're following #DR's point about keeping values out of the GUI (in a sense), you're using a storage field in each label instead.
eg;
when setting a score;-
Label1.Caption:=Format('%d point',[FScore]);
Label1.Tag:=FScore;
and when summing them;-
FSum:=Label1.Tag + Label2.Tag + Label3.Tag (etc)

Resources