скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

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.net).


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.






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook