Работа с OLE Automation из PERL'а
Чак Богорад
Работа с OLE Automation из PERL'а
Эта статья предназначена для тех, кто уже умеет сносно программировать на
PERL, и хочет получить навыки работы с OLE Automation. В приводимых примерах
использовался так называемый PERL-GS, который можно взять уже собранным, или
собрать из исходных текстов на http://www.perl.com/CPAN/ (не путать с
ActiveState PERL, доступным на http://www.activestate.com). Для работы с OLE
Automation используется модуль Win32::OLE, который входит в библиотеку libwin32,
также доступную с CPAN.
Модуль Win32::OLE (как и большинство других модулей) использует объектную
модель. В PERL она организована очень просто: объект - всего лишь ссылка на
HASH, в котором могут быть данные, ссылки на данные или ссылки на процедуры.
Вызов "метода" - всего лишь разименование ссылки на HASH, еще одно разименование
ссылки на процедуру и вызов этой процедуры. Подробнее об этом и массе других
интересных вещей можно прочитать в книжке "Advanced PERL programming", которую
написал Sriram Srinivasan. Купить ее можно здесь:
http://www.amazon.com/exec/obidos/ASIN/1565922204/002-9864635-4316215 или в
некоторых московских книжных магазинах (там, правда, она стоит чуть дороже).
Кратко опишу идеи OLE Automation, как я его понимаю. Сразу хочу предупредить
- это может не иметь почти ничего общего с идеей его создателей. Итак, OLE
Automation - это единый интуитивно понятный интерфейс, построенный по образу и
подобию интерфейса всех windows-программ. Интерфейс объектный, то есть
существуют объекты, их атрибуты и методы. Работая с ним, мы фактически имитируем
работу с программой через её меню. Это гораздо удобнее, чем изучать для каждой
программы ее собственный API. Так, показ WEB-страницы в MS IE и удаление старого
письма из папки MS Outlook - одинаково примитивные и похожие операции.
Теперь к первому примеру. Это пример из жизни, задача состояла в том, чтобы
преобразовать очень много (несколько сотен) документов из MS RTF в обычные
текстовые файлы. Сразу замечу, что использовать самодельные преобразователи было
нельзя, так как RTF - формат плохо документированный и непостоянный, поэтому
едиственный надежный способ - использовать MS Word. Сначала рассмотрим, как эта
задача решается при помощи MS Word. Мы загружаем файл ( File -> Open), затем
сохраняем его как текстовый (File -> SaveAs -> DOS Text). Точно такие же
действия производятся при помощи OLE Automation.
#!perl -w
# Всегда используем 'use strict' и параметр '-w' , чтобы избежать ошибок и
# облегчить оптимизацию.
use strict;
# Модуль 'Win32::OLE' объявляет нужные объекты.
use Win32::OLE;
# Модуль 'Win32::OLE:Const' объявляет нужные константы.
use Win32::OLE::Const 'Microsoft Word';
{
# Локальные переменные для имен входного и выходного файлов.
my ($file_in, $file_out);
# Используем 't.rtf' в качестве имени файла, если не задано другое имя.
$file_out = $file_in = $ARGV[0] ? $ARGV[0] : 't.rtf';
# Формируем имя выходного файла.
$file_out =~ s/(.*.).*/$1txt/;
# Создаем объект - 'программа Word'.
# Если Word не был активен, он будет запущен.
my $word = Win32::OLE->new('Word.Application', 'Quit');
# Создаем объект 'документ Word'. Обратите внимание - единственным
# параметром 'методу' Open является ссылка на HASH. При открытии файла
# можно указать разнообразные параметры.
my $doc = $word->Documents->Open({
FileName => $file_in,
ConfirmConversions => 0,
ReadOnly => 1,
AddToRecentFiles => 0,
Format => wdOpenFormatAuto});
# Теперь сохраняем файл так DOS Text.Обратите внимание - единственным
# параметром 'методу' SaveAs является ссылка на HASH.
my $rc = $doc->SaveAs({
FileName => $file_out,
FileFormat => wdFormatDOSTextLineBreaks,
AddToRecentFiles => 0});
# Закрываем документ.
$rc = $doc->Close;
# Завершаем работу MS Word.
$rc = $word->Quit;
};
Следующий пример тоже жизненный - удаление дублированных писем из папок MS
Outlook. Дело в том, что у меня дома отвратительная телефонная линия, связь все
время рвется, и Outlook не успевает забрать все письма из POP3-ящика. И каждый
раз начинает сначала. Таким образом, накапливается огромное количество
одинаковых писем, и их удаление - задача неприятная.
Используем Perl + OLE Automation. Перебираем все письма в папке, используем
дату/время получения письма как ключ в HASH. В качестве элемента HASH заносим
строку, полученную путем склеивания полей from/to/date/size. Если такая запись
уже есть, удаляем текущее письмо. Аналогично для всех остальных папок. У меня
все mail-list'ы складываются в подпапки папки 'Листы разные'.
#!perl -w
# Всегда используем 'use strict' и параметр '-w' , чтобы избежать ошибок и
# облегчить оптимизацию.
use strict;
# Используем модуль национальной поддержки.
use locale;
# Модуль 'Win32::OLE' объявляет нужные объекты.
use Win32::OLE;
# Модуль 'Win32::OLE:Const' объявляет нужные константы.
use Win32::OLE::Const 'Microsoft Outlook';
{ #main
# Сохраняем текущую кодовую старницу.
my $cp = `chcp`;
# Выделяем число из 'Active code page: 866'
chomp($cp);
$cp =~ s/.*?(d+).*/$1/;
# Временно устанавливаем Windows CP.
`chcp 1251`;
# Создаем объект 'программа Outlook'.
my $olApp = Win32::OLE->new('Outlook.Application');
# Получаем handle(MAPI).
my $nameSpace = $olApp->GetNameSpace('MAPI');
# Получаем handle папки 'Personal Folders'. ВНИМАНИЕ: в русском Outlook'е
# она называется 'Личные папки'.
my $persFolder = $nameSpace->Folders('Personal Folders');
# Получаем handle папки 'Листы разные'.
my $listsFolder = $persFolder->Folders('Листы разные');
# Получаем handles всех ее подпапок.
# Обратите внимание, что 'метод' 'Folders'
# возвращает scalar, а не list!
my $listFolders = $listsFolder->Folders();
# Перебираем сообщения в каждой подпапке.
my $curFolder;
# Чтобы получить список всех handles
# всех подпапок используется функция 'in'.
foreach $curFolder (in $listFolders)
{
# Выводим имя обрабатываемой папки
print $curFolder->Name(), qq(n);
# Заводим HASH, где будут храниться ключи.
my %dupes;
# Перебираем все письма в подпапке. 'Items' возвращает scalar.
my $curItems = $curFolder->Items();
my $curItem;
# Снова используем функцию 'in' для получения list'а.
foreach $curItem (in $curItems)
{
# Берем время прихода письма.
my $msgTime = $curItem->ReceivedTime();
# Склеиваем остальные поля.
my $msgID = $curItem->SentOnBehalfOfName() .
$curItem->To() .
$curItem->SentOn() .
$curItem->Size();
# Проверяем, встречалось ли уже такое письмо.
if (defined($dupes{$msgTime}) and ($dupes{$msgTime} eq $msgID))
{
# Если встречалось - стираем текущее письмо.
$curItem->Delete();
print qq(Deleted $msgIDn);
}
else
{
# В противном случае заполняем HASH.
$dupes{$msgTime} = $msgID;
};
};
}; # foreach
print qq(Quitting!n);
# Здесь можно выйти из MS Outlook.
# $olApp->Quit();
# Восстанавливаем исходную CODEPAGE.
system("chcp $cp");
exit(0);
} #main
Еще одной чрезвычайно интересной задачей является написание скрипта, который
бы посылал напоминания о грядущих событиях на пейджер. Со временем и такой
несомненно появится.
В заключение хочу обратить внимание на источники информации о структуре
объектов OLE Automation в MS Office. В принципе, все необходимое есть в HELP for
Visual basic. Кое-что есть в help-файлах, лежащих в папке MOREHELP на CD с MS
Office. В Internet тоже есть места с информацией об объектах OLE Automation:
|