Word, OutLook и OLE



Здесь мы ответим на действительно интересные вопросы:

  • Как узнать, установлен ли Word 8 на машине клиента?
  • Где расположены шаблоны?
  • Почему запускается все время новый документ, когда я хочу работать в том же?
  • Как найти документ, с которым пользователь работал в последнее время?
  • Почему Word закрывается после завершения моей процедуры?
  • Как мне добраться до папок программы Outlook?
  • Как в Outlook получить доступ к существующему контакту или создать свой?

{--------------------Взято из библиотеки типов--------------- WORDDEC.INC}
Const
// OlAttachmentType
olByValue = 1;
olByReference = 4;
olEmbeddedItem = 5;
olOLE = 6;
// OlDefaultFolders
olFolderDeletedItems = 3;
olFolderOutbox = 4;
olFolderSentMail = 5;
olFolderInbox = 6;
olFolderCalendar = 9;
olFolderContacts = 10;
olFolderJournal = 11;
olFolderNotes = 12;
olFolderTasks = 13;
// OlFolderDisplayMode
olFolderDisplayNormal = 0;
olFolderDisplayFolderOnly = 1;
olFolderDisplayNoNavigation = 2;
// OlInspectorClose
olSave = 0;
olDiscard = 1;
olPromptForSave = 2;
// OlImportance
olImportanceLow = 0;
olImportanceNormal = 1;
olImportanceHigh = 2;
// OlItems
olMailItem = 0;
olAppointmentItem = 1;
olContactItem = 2;
olTaskItem = 3;
olJournalItem = 4;
olNoteItem = 5;
olPostItem = 6;
// OlSensitivity
olNormal = 0;
olPersonal = 1;
olPrivate = 2;
olConfidential = 3;
// OlJournalRecipientType;
olAssociatedContact = 1;
// OlMailRecipientType;
olOriginator = 0;
olTo = 1;
olCC = 2;
olBCC = 3;
Const
wdGoToBookmark = -1;
wdGoToSection = 0;
wdGoToPage = 1;
wdGoToTable = 2;
wdGoToLine = 3;
wdGoToFootnote = 4;
wdGoToEndnote = 5;
wdGoToComment = 6;
wdGoToField = 7;
wdGoToGraphic = 8;
wdGoToObject = 9;
wdGoToEquation = 10;
wdGoToHeading = 11;
wdGoToPercent = 12;
wdGoToSpellingError = 13;
wdGoToGrammaticalError = 14;
wdGoToProofreadingError = 15;
wdGoToFirst = 1;
wdGoToLast = -1;
wdGoToNext = 2;   //интересно,
wdGoToRelative = 2;  //чем отличаются эти две константы?
wdGoToPrevious = 3;
wdGoToAbsolute = 1;

Основные функции:


Function GetWordUp(StartType : string):Boolean;
Function InsertPicture(AFileName : String) : Boolean;
Function InsertContactInfo(MyId : TMyId; MyContId : TMyContId): Boolean;
Function GetOutlookUp(ItemType : Integer): Boolean;
Function MakeOutLookContact(MyId : TMyId; MyContId : TMyContId) : Boolean;
Function ImportOutlookContact : Boolean;
Function GetOutlookFolderItemCount : Integer;
Function GetThisOutlookItem(AnIndex : Integer) : Variant;
Function FindMyOutlookItem(AFilter : String; var AItem : Variant) :Boolean;
Function FindNextMyOutlookItem(var AItem : Variant) : Boolean;
Function CloseOutlook : Boolean;
Type TTreeData = class(TObject)
Public
ItemId : String;
end;


{$I worddec.inc} {все константы из библиотеки типов тащим с собой}
var
myRegistry: TRegistry;
GotWord: Boolean;
WhereIsWord: string;
WordDoneMessage: Integer;
Basically: variant;
Wordy: Variant;
MyDocument: Variant;
MyOutlook: Variant;
MyNameSpace: Variant;
MyFolder: Variant;
MyAppointment: Variant;
function GetWordUp(StartType: string): Boolean;
// Запускаем Word "правильным" на мой взгляд способом
// после старта Word мы сделаем так, чтобы после завершения приложения он остался открытым
var
i: integer;
AHwnd: Hwnd;
AnAnswer: Integer;
temp: string;
MyDocumentsCol: Variant;
TemplatesDir: Variant;
OpenDialog1: TopenDialog;
begin
result := false;
myRegistry := Tregistry.Create;
myRegistry.RootKey := HKEY_LOCAL_MACHINE;
// никакого "word 8", никакой функции!
if myRegistry.KeyExists('SOFTWARE\Microsoft\Office\8.0\Word') then
GotWord := true
else
GotWord := false;
if GotWord then
//где он, черт побери?
if myRegistry.OpenKey('SOFTWARE\Microsoft\Office\8.0', false) then
begin
WhereisWord := myRegistry.ReadString('BinDirPath');
MyRegistry.CloseKey;
end
else
GotWord := false;
if GotWord then
//и где эти надоевшие шаблоны?
begin
MyRegistry.RootKey := HKEY_CURRENT_USER;
if
myRegistry.OpenKey('SOFTWARE\Microsoft\Office\8.0\Common\FileNew\SharedTemplates', false) then
begin
TemplatesDir := myRegistry.ReadString(Nothing);
MyRegistry.CloseKey;
end
else
begin
Warning('Ole инсталляция', 'Шаблоны рабочей группы не установлены');
GotWord := false;
end;
end;
myRegistry.free;
if not gotword then
begin
Warning('Ole дескриптор', 'Word не установлен');
exit;
end;
//это имя класса принадлежит главному окну в двух последних версиях Word
temp := 'OpusApp';
AHwnd := FindWindow(pchar(temp), nil);
if (AHwnd = 0) then
//Word не запущен, пробуем запустить пустую оболочку без документа
begin
Temp := WhereisWord + '\winword.exe /n';
AnAnswer := WinExec(pchar(temp), 1);
if (AnAnswer < 32) then
begin
Warning('Ole дескриптор', 'Не могу найти WinWord.exe');
Exit;
end;
end;
Application.ProcessMessages;
{Если вы уже используете Word.Application, вы получаете ваш собственный экземпляр}
{Если вы уже используете Word.Document, вы получаете работающий экземпляр}
{по-моему все понятно и очень удобно (во всяком случае мне)}
try {создаем новый документ}
Basically := CreateOleObject('Word.Document.8');
except
Warning('Ole дескриптор', 'Не могу запустить Microsoft Word.');
Result := False;
Exit;
end;
try {ссылаемся в переменной вариантного на вновь созданный документ}
Wordy := Basically.Application;
except
begin
Warning('Ole дескриптор', 'Не могу получить доступ к Microsoft Word.');
Wordy := UnAssigned;
Basically := UnAssigned;
Exit;
end;
end;
Application.ProcessMessages;
Wordy.visible := false;
MyDocumentsCol := Wordy.Documents;
{Проверяем количество открытых документов и пытаемся вывести диалог выбора шаблона}
if (MyDocumentsCol.Count = 1) or
(StartType = 'New') then
begin
OpenDialog1 := TOpenDialog.Create(Application);
OpenDialog1.filter := 'Шаблоны Word|*.dot|Документы Word|*.doc';
OpenDialog1.DefaultExt := '*.dot';
OpenDialog1.Title := 'Выберите ваш шаблон';
OpenDialog1.InitialDir := TemplatesDir;
if OpenDialog1.execute then
begin
Wordy.ScreenUpdating := false;
MyDocumentsCol := wordy.Documents;
MyDocumentsCol.Add(OpenDialog1.Filename, False);
OpenDialog1.free;
end
else
begin
OpenDialog1.Free;
Wordy.visible := true;
Wordy := Unassigned;
Basically := Unassigned;
Exit;
end;
end
else
{закрываем документ}
MyDocument.close(wdDoNotSaveChanges);
{теперь мы имеем или новый документ на основе шаблона, выбранного пользователем
или же его текущий документ}
MyDocument := Wordy.ActiveDocument;
Result := true;
Application.ProcessMessages;
end;
function InsertPicture(AFileName: string): Boolean;
var
MyShapes: Variant;
MyRange: variant;
begin
Result := True;
if GetWordUp('Current') then
try
begin
MyRange := MyDocument.goto(wdgotoline, wdgotolast);
MyRange.EndOf(wdParagraph, wdMove);
MyRange.InsertBreak(wdPageBreak);
MyShapes := MyDocument.InlineShapes;
MyShapes.AddPicture(afilename, false, true, MyRange);
end;
finally
begin
Wordy.ScreenUpdating := true;
Wordy.visible := true;
Wordy := Unassigned;
Basically := UnAssigned;
Application.ProcessMessages;
end;
end
else
Result := False;
end;
function InsertContactInfo(MyId: TMyId; MyContId: TMyContId): Boolean;
var
MyCustomProps: Variant;
begin
{ лично я сначала сохраняю свою визитку в свойствах документа, а только
потом вывожу панели с инструментами для того, чтобы пользователь мог
"установить" принадлежность шаблона или текущего документа.
на мой взгляд здесь есть три достоинства (здесь нет подвохов, уверяю вас):
1. Пользователь может установить свои свойства документа после того,
как функция отработает
2. Другие свойства могут быть установлены в любом месте
того же документа
3. Пользователь может переслать эти свойства в тот же Outlook или с их
помощью найти документ, используя функции расширенного поиска Word}
Result := true;
if GetWordUp('New') then
try
begin
MyCustomProps := MyDocument.CustomDocumentProperties;
MyCustomProps.add(cpId, false, msoPropertyTypeString, MyId.Id);
MyCustomProps.add(cpOrganizationName,
false, msoPropertyTypeString, MyId.OrganizationName);
MyCustomProps.add(cpAddress1,
false, msoPropertyTypeString, MyId.Address1);
MyCustomProps.add(cpAddress2, false,
msoPropertyTypeString, MyId.Address2);
MyCustomProps.add(cpCity, false,
msoPropertyTypeString, MyId.City);
MyCustomProps.add(cpStProv, false,
msoPropertyTypeString, MyId.StProv);
MyCustomProps.add(cpCountry,
false, msoPropertyTypeString, MyId.City);
MyCustomProps.add(cpPostal, false,
msoPropertyTypeString, MyId.Country);
MyCustomProps.add(cpAccountId, false,
msoPropertyTypeString, MyId.AccountId);
MyCustomProps.add(cpFullName, false,
msoPropertyTypeString, MyContId.FullName);
MyCustomProps.add(cpSalutation, false,
msoPropertyTypeString, MyContId.Salutation);
MyCustomProps.add(cpTitle, false,
msoPropertyTypeString, MyContId.Title);
if (MyContId.workPhone = Nothing) or
(MycontId.WorkPhone = ASpace) then
MyCustomProps.add(cpPhone, false,
msoPropertyTypeString, MyId.Phone)
else
MyCustomProps.add(cpPhone, false,
msoPropertyTypeString, MyContId.WorkPhone);
if (MyContId.Fax = Nothing) or (MycontId.Fax = ASpace) then
MyCustomProps.add(cpFax, false,
msoPropertyTypeString, MyId.Fax)
else
MyCustomProps.add(cpFax, false,
msoPropertyTypeString, MyContId.Fax);
if (MyContId.EMail = Nothing) or (MycontId.Email = ASpace) then
MyCustomProps.add(cpEmail, false,
msoPropertyTypeString, MyId.Email)
else
MyCustomProps.add(cpEmail, false,
msoPropertyTypeString, MyContId.Email);
MyCustomProps.add(cpFirstName, false,
msoPropertyTypeString, MyContId.FirstName);
MyCustomProps.add(cpLastName, false,
msoPropertyTypeString, MyContId.LastName);
MyDocument.Fields.Update;
end;
finally
begin
Wordy.ScreenUpdating := true;
Wordy.visible := true;
Wordy := Unassigned;
Basically := UnAssigned;
Application.ProcessMessages;
end;
end
else
Result := false;
end;
function GetOutlookUp(ItemType: Integer): Boolean;
const
AppointmentItem = 'Calendar';
TaskItem = 'Tasks';
ContactItem = 'Contacts';
JournalItem = 'Journal';
NoteItem = 'Notes';
var
MyFolders: Variant;
MyFolders2: variant;
MyFolders3: variant;
MyFolder2: Variant;
MyFolder3: variant;
MyUser: Variant;
MyFolderItems: Variant;
MyFolderItems2: Variant;
MyFolderItems3: Variant;
MyContact: Variant;
i, i2, i3: Integer;
MyTree: TCreateCont;
MyTreeData: TTreeData;
RootNode, MyNode, MyNode2: ttreeNode;
ThisName: string;
begin
{это действительно безобразие........
В Outlook несколько странно реализована объектная модель,
и такие перлы как folder.folder.folder считаются "верным решением"
для получения доступа к папкам этой великолепной программы.}
{пользователь выбирает папку из дерева папок}
Result := False;
case ItemType of
olAppointmentItem: ThisName := AppointmentItem;
olContactItem: ThisName := ContactItem;
olTaskItem: ThisName := TaskItem;
olJournalItem: ThisName := JournalItem;
olNoteItem: ThisName := NoteItem;
else
ThisName := 'Unknown';
end;
try
MyOutlook := CreateOleObject('Outlook.Application');
except
warning('Ole интерфейс', 'Не могу запустить Outlook.');
Exit;
end;
{это папка верхнего уровня}
MyNameSpace := MyOutlook.GetNamespace('MAPI');
MyFolderItems := MyNameSpace.Folders;
MyTree := TCreateCont.create(Application);
{Действительно неудачно, ведь пользователь может создать что-то другое,
чем папки, предлагаемые по-умолчанию, на которые мы и хотели опереться
в нашей программе, поэтому перемещаемся на нижний уровень в цепочке папок}
MyTree.Caption := 'Выбрана ' + ThisName + ' папка';
with MyTree do
if MyFolderItems.Count > 0 then
for i := 1 to MyFolderItems.Count do
begin
MyFolder := MyNameSpace.Folders(i);
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder.EntryId;
RootNode := TreeView1.Items.AddObject(nil, MyFolder.Name, MyTreeData);
MyFolders2 := MyNameSpace.folders(i).Folders;
if MyFolders2.Count > 0 then
for i2 := 1 to MyFolders2.Count do
begin
MyFolder2 := MyNameSpace.folders(i).Folders(i2);
if (MyFolder2.DefaultItemType = ItemType)
or (MyFolder2.Name = ThisName) then
begin
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder2.EntryId;
{вот мы и добрались непосредственно до папок}
MyNode :=
Treeview1.Items.addChildObject(RootNode, MyFolder2.Name,
MyTreeData);
MyFolders3 :=
MyNameSpace.folders(i).Folders(i2).Folders;
if MyFolders3.Count > 0 then
for i3 := 1 to MyFolders3.Count do
begin
MyFolder3 := MyNameSpace.folders(i).Folders(i2).Folders(i3);
if (MyFolder3.DefaultItemType = ItemType) then
begin
MyTreeData := TTreeData.create;
MyTreeData.ItemId := MyFolder3.EntryId;
MyNode2 :=
Treeview1.Items.addChildObject(MyNode, MyFolder3.Name,
MyTreeData);
end;
end;
end;
end;
end;
if MyTree.TreeView1.Items.Count = 2 then
{есть только корневая папка и папка, определенная мной}
MyFolder :=
MyNameSpace.GetFolderFromID(TTreeData(MyTree.TreeView1.Items[1].Data).ItemId
)
else
begin
MyTree.Treeview1.FullExpand;
MyTree.ShowModal;
if MyTree.ModalResult = mrOk then
begin
if MyTree.Treeview1.Selected <> nil then
MyFolder :=
MyNameSpace.GetFolderFromID(TTreeData(MyTree.Treeview1.Selected.Data).ItemId
);
end
else
begin
MyOutlook := UnAssigned;
for i := MyTree.Treeview1.Items.Count - 1 downto 0 do
TTreeData(MyTree.Treeview1.Items[i].Data).free;
MyTree.release;
exit;
end;
end;
for i := MyTree.Treeview1.Items.Count - 1 downto 0 do
TTreeData(MyTree.Treeview1.Items[i].Data).free;
MyTree.release;
Result := true;
end;
function MakeOutlookContact(MyId: TMyId; MyContId: TMyContId): boolean;
var
MyContact: Variant;
begin
Result := false;
if not GetOutlookUp(OlContactItem) then
exit;
MyContact := MyFolder.Items.Add(olContactItem);
MyContact.Title := MyContId.Honorific;
MyContact.FirstName := MyContId.FirstName;
MyContact.MiddleName := MycontId.MiddleInit;
MyContact.LastName := MycontId.LastName;
MyContact.Suffix := MyContId.Suffix;
MyContact.CompanyName := MyId.OrganizationName;
MyContact.JobTitle := MyContId.Title;
MyContact.OfficeLocation := MyContId.OfficeLocation;
MyContact.CustomerId := MyId.ID;
MyContact.Account := MyId.AccountId;
MyContact.BusinessAddressStreet := MyId.Address1 + CRLF + MyId.Address2;
MyContact.BusinessAddressCity := MyId.City;
MyContact.BusinessAddressState := MyId.StProv;
MyContact.BusinessAddressPostalCode := MyId.Postal;
MyContact.BusinessAddressCountry := MyId.Country;
if (MyContId.Fax = Nothing) or (MyContId.Fax = ASpace) then
MyContact.BusinessFaxNumber := MyId.Fax
else
MyContact.BusinessFaxNumber := MyContId.Fax;
if (MyContId.WorkPhone = Nothing) or (MyContId.WorkPhone = ASpace) then
MyContact.BusinessTelephoneNumber := MyId.Phone
else
MyContact.BusinessTelephoneNumber := MyContId.WorkPhone;
MyContact.CompanyMainTelephoneNumber := MyId.Phone;
MyContact.HomeFaxNumber := MyContId.HomeFax;
MyContact.HomeTelephoneNumber := MyContId.HomePhone;
MyContact.MobileTelephoneNumber := MyContId.MobilePhone;
MyContact.OtherTelephoneNumber := MyContId.OtherPhone;
MyContact.PagerNumber := MyContId.Pager;
MyContact.Email1Address := MyContId.Email;
MyContact.Email2Address := MyId.Email;
Result := true;
try
MyContact.Save;
except
Result := false;
end;
MyOutlook := Unassigned;
end;
function GetThisOutlookItem(AnIndex: Integer): Variant;
begin
Result := myFolder.Items(AnIndex);
end;
function GetOutlookFolderItemCount: Integer;
var
myItems: Variant;
begin
try
MyItems := MyFolder.Items;
except
begin
Result := 0;
exit;
end;
end;
Result := MyItems.Count;
end;
function FindMyOutlookItem(AFilter: string; var AItem: Variant):
Boolean;
begin
{не забудьте предварительно инициализировать AItem значением NIL}
Result := true;
try
AItem := myFolder.Items.Find(AFilter);
except
begin
aItem := MyFolder;
Result := false;
end;
end;
end;
function FindNextMyOutlookItem(var AItem: Variant): Boolean;
begin
Result := true;
try
AItem := myFolder.Items.FindNext;
except
begin
AItem := myFolder;
Result := false;
end;
end;
end;
function CloseOutlook: Boolean;
begin
try
MyOutlook := Unassigned;
except
end;
Result := true;
end;

Как использовать весь этот код?
Вот модуль для работы с Контактами программы Outlook.
Строим расширенный список контактов (компонент TExtListView вы можете найти на www.torry.ru).


unit UImpContact;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
UMain, StdCtrls, Buttons, ComCtrls, ExtListView;
type
TFindContact = class(TForm)
ContView1: TExtListView;
SearchBtn: TBitBtn;
CancelBtn: TBitBtn;
procedure SearchBtnClick(Sender: TObject);
procedure CancelBtnClick(Sender: TObject);
procedure ContView1DblClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
var
FindContact: TFindContact;
implementation
uses USearch;
{$R *.DFM}
procedure TFindContact.SearchBtnClick(Sender: TObject);
begin
if ContView1.Selected <> nil then
ContView1DblClick(nil);
end;
procedure TFindContact.CancelBtnClick(Sender: TObject);
begin
CloseOutlook;
ModalResult := mrCancel;
end;
procedure TFindContact.ContView1DblClick(Sender: TObject);
var
MyContact: variant;
begin
if ContView1.Selected <> nil then
begin
MyContact := GetThisOutlookItem(StrToInt(ContView1.Selected.subitems[2]));
with StartForm.MyId do
if not GetData(MyContact.CustomerId) then
begin
InitData;
if MyContact.CustomerId <> '' then
Id := MyContact.CustomerId
else
Id := MyContact.CompanyName;
if DoesIdExist(Startform.MyId.Id) then
begin
Warning('Дескриптор данных', 'Не могу установить уникальный Id' + CRLF
+ 'Отредактируйте CustomerId в Outlook и попытайтесь снова');
CloseOutlook;
ModalResult := mrCancel;
Exit;
end;
OrganizationName := MyContact.CompanyName;
IdType := 1;
AccountId := MyContact.Account;
Address1 := MyContact.BusinessAddressStreet;
City := MyContact.BusinessAddressCity;
StProv := MyContact.BusinessAddressState;
Postal := MyContact.BusinessAddressPostalCode;
Country := MyContact.BusinessAddressCountry;
Phone := MyContact.CompanyMainTelephoneNumber;
Insert;
end;
with StartForm.MyContId do
begin
InitData;
ContIdId := StartForm.MyId.Id;
Honorific := MyContact.Title;
FirstName := MyContact.FirstName;
MiddleInit := MyContact.MiddleName;
LastName := MyContact.LastName;
Suffix := MyContact.Suffix;
Fax := MyContact.BusinessFaxNumber;
WorkPhone := MyContact.BusinessTelephoneNumber;
HomeFax := MyContact.HomeFaxNumber;
HomePhone := MyContact.HomeTelephoneNumber;
MobilePhone := MyContact.MobileTelephoneNumber;
OtherPhone := MyContact.OtherTelephoneNumber;
Pager := MyContact.PagerNumber;
Email := MyContact.Email1Address;
Title := MyContact.JobTitle;
OfficeLocation := MyContact.OfficeLocation;
Insert;
end;
end;
CloseOutlook;
ModalResult := mrOk;
end;
procedure TFindContact.FormCreate(Sender: TObject);
var
MyContact: Variant;
MyCount: Integer;
i: Integer;
AnItem: TListItem;
begin
if not GetOutlookUp(OlContactItem) then
exit;
MyCount := GetOutlookFolderItemCount;
for i := 1 to MyCount do
begin
MyContact := GetThisOutlookItem(i);
AnItem := ContView1.Items.Add;
AnItem.Caption := MyContact.CompanyName;
AnItem.SubItems.add(MyContact.FirstName);
AnItem.Subitems.Add(MyContact.LastName);
AnItem.SubItems.Add(inttostr(i));
end;
end;
procedure TFindContact.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
Action := cafree;
end;
end.


Далее: Wordbasic с параметрами из Delphi »»