Эта статья впервые была напечатана в ClarionOnline ( http://www.clariononline.com ) том 2 выпуск 10 (май 1999)


Работа с email через Simple MAPI

Компьютеры и интернет становятся все более распространенными в обществе, люди все более привычно используют email, веб и т.п. Одна из вещей, которую вы должны сделать как разработчик - это добавить поддержку email в ваши программы.

Как и в большинстве вещей в жизни, имеется много средств для достижения одной и той же цели. Для случае поддержки email, самый простой способ это использовать уже имеющиеся инструменты третьих фирм. Однако, я предпочитаю использовать мой собственный интерфейс - это может занять больше времени чем хотелось бы, но я должен иметь полный контроль за всем и полностью понять всю поддерживающую философию.

К сожалению, как и в большинстве вещей, здесь есть что изучать. Когда вы поймете базовые принципы, то все становится легко.

Какой интерфейс?

Есть несколько разных интерфейсов, которые может использовать разработчик для добавления функций email в свое приложение. К сожалению, не все из этих возможностей доступны Clarion-разработчику. Вот что вы можете использовать и почему:

MAPI (Mail API)

Это базовая почтовая система, на которой базируются другие три. Она предоставляет очень большой контроль за всеми аспектами почтовой системы, но это полностью объектно-ориентированная и COM-совместимая реализация. Для нас как для разработчиков это означает (имея в виду что Clarion в настоящее время не совместим с COM) что в основном мы ее не можем использовать. Если имеются функции, которые вы хотите чтобы другие возможности не могли адресовать, то вероятно есть VBX или OCX, которые это могут.
 

CMC (Common Messaging Calls) и Simple MAPI (Simple Mail API)

CMC и SMAPI предоставляют простой набор из дюжины высокоуровневых вызовов API, которые могут использовать Clarion разработчики. Функции предоставляемые SMAPI и CMC практически идентичны, но CMC немного компактнее, он объединяет много возможности в одной функции. Хотя реализация Microsoft основана на MAPI, сам CMC API был разработан в объединении с ассоциацией стандартов X.400 API, и поддерживается в платформах Windows, MS-DOS, OS/2, Macintosh, и UNIX. 
 

AML (Active Message Library)

AML является программным интерфейсом OLE automation, который является компонентом интерфейса MAPI. AML предоставляет программируемые объекты, подобные объектам Microsoft Excel и объектам Microsoft Access, что делает доступными свойства и методы, которые могут управляться при помощи программ на Visual Basic (VB) и Visual Basic for Applications (VBA). И опять, из-за того что Clarion не поддерживает COM и OLE (I’m being PC here), это означает что мы не можем их использовать.

В результате нам остается выбор из CMC и SMAPI. Как уже сказано выше, они практически одинаковы, исключая то что CMC является кросс-платформеным а SMAPI нет.

Имея это в виду, я собираюсь описать основы использования Simple MAPI (SMAPI) в программах на Clarion. Имеется 12 функций Simple MAPI - я собираюсь использовать 5 из них для отсылки и приема почты. Они все практически одинаковы, поэтому если вы хотите использовать одну из тех, что я не описал, то они получают параметры подобные приведенным ниже

Функция Simple MAPI Описание
MAPIAddress Адресовать сообщение
MAPIDeleteMail Удалить сообщение
MAPIDetails Показать диалоговое окно recipient-details
MAPIFindNext Возвращает идентификатор первого или следующего сообщения указанного типа
MAPIFreeBuffer Освобождает память, выделенную системой сообщений
MAPILogoff Завершает сессию системы сообщений 
MAPILogon Устанавливает систему сообщений
MAPIReadMail Прочитать сообщение 
MAPIResolveName Выводит диалоговое окно разрешения неоднозначного имени получателя 
MAPISaveMail Сохраняет сообщение 
MAPISendDocuments Посылает стандартное сообщение с использованием диалогового окна
MAPISendMail Посылает сообщение, предоставляя большую гибкость в формировании сообщения чем MAPISendDocuments() 

Установлен ли клиентский интерфейс?

Хотя мы только что определились с нашим почтовым интерфейсом, не все компьютеры могут иметь уже установленным этот интерфейс, поэтому мы должны определить имеется ли он и запретить соответствующие пункты меню если его нет. Мы можем проверить наличие каждого их 4-х почтовых интерфейсов используя процедуру GETINI для файла WIN.INI. Соответствующая секция ini-файла будет похожа на следующую. Если интерфейс установлен, то ее опция будет установлена в 1, иначе она будет равна 0. 

[Mail]
;MAPI
MAPIX=0 or 1
;CMC
CMC=0 or 1
;Simple MAPI
MAPI=0 or 1
;AML
OLEMessaging=0 or 1

Ваша программа, когда обнаружит присутствие нужного интерфейса, должна вызвать функции LoadLibrary() и GetProcAddress() для соответствующих DLL. После этого мы можем вызывать нужные нам процедуры. С другой стороны, гораздо проще прилинковать к программе библиотеку импорта MAPI статически. Конечно, если мы так сделали а MAPI не установлена, то приложение не сможет запуститься. Итак, что же мы делаем? Правильнее не прилинковывать MAPI.DLL статически (для случая если он не установлен). 

Моя теория такова. Во первых, каждый новый компьютер приходит с предустановленной Windows, и поддержка email виртуально гарантирована. Во вторых, если вы делаете поддержку email по запросу клиента, то он уже должен иметь какую-либо систему email уже установленную. 

Как вы уже вероятно поняли, я линкую свою программу а не импортирую библиотеку. Не ищите ее на своем компьютере, так как вероятно вы ее не найдете. Она не поставляется с Clarion, так как Clarion не декларирует поддержку SMAPI , и ее нет в вашем каталоге \Windows, так как Microsoft не имеет привычки поставлять библиотеки импорта для своих DLL. Для получения нужного LIB-файла вы должны воспользоваться LibMaker-ом. Это удобная утилита поставляется со всеми последними версиями Clarion в той или другой форме. Она просто создает LIB-файлы, которые вы можете ликовать вместо оригинальной DLL. Запустите LibMaker и найдите MAPI32.DLL (или MAPI.DLL если вы делаете 16-разрядную программу). Вероятно, она находится в \WINDOWS\SYSTEM (или в \WINNT\SYSTEM под Windows NT).

Сессии

Взаимодействие между вашей клиентской программой и подсистемой SMAPI основано на концепции сессий. Перед тем как ваша программа сможет вызывать низкоуровневую систему сообщений, она должна установить сессию, или соединение, с подсистемой MAPI. Сессия инициализируется когда пользователь "логинится", убеждается что существует доступ к правильному профилю, подтверждает полномочия системы сообщений и службы сообщений, и страхуется против того что все профили службы сообщений правильно сконфигурированы.

Клиенты могут указать один из двух типов сессий для установки при вызове logon: индивидуальную сессию или разделяемая (shared). Индивидуальные сессии являются приватными соединениями; они используют соединение "один к одному" между клиентской программой и сессией. Как следствие, клиентские программы разделяющие сессию также разделяют профиль. Разделяемые сессии устанавливаются однажды, но могут "использоваться" другими клиентскими программами, которые нуждаются в них. 

Logging on и создание новой MAPI сессии

Для создания новой сессии нужно использовать функцию MAPILogon(). Ее API прототип:

ULONG FAR PASCAL MAPILogon( ULONG ulUIParam, 
LPTSTR lpszProfileName, 
LPTSTR lpszPassword, 
FLAGS flFlags, 
ULONG ulReserved, 
LPLHANDLE lplhSession )

Clarion прототип:

MAPILogon(ULONG,ULONG,ULONG,ULONG,ULONG,*ULONG),ULONG,RAW,PASCAL

Параметры имеют следующие объяснение:

MAPI_FORCE_DOWNLOAD ытаться загрузить все пользовательские сообщения перед возвратом. Если признак MAPI_FORCE_DOWNLOAD не установлен, то сообщения могут быть загружены в фоновом режиме после того как произойдет возврат из функции.
MAPI_NEW_SESSION Пытаться создать новую сессию до того как получена среда (environment's) разделяемой сессии. Если признак MAPI_NEW_SESSION не установлен, то MAPILogon использует существующую разделяемую сессию.
MAPI_LOGON_UI Должен быть выведен logon dialog box для ввода пользователем logon information. Если пользователь должен сообщить пароль и имя профайла, то признак MAPI_LOGON_UI должен быть включен.
MAPI_PASSWORD_UI MAPILogon должен только ввести пароль и не давать пользователю изменить имя профайла. Признак MAPI_PASSWORD_UI или MAPI_LOGON_UI не должен быть установлен, так как нужно выбрать для logon один из двух разных диалоговых окон.

lplhSession - содержит хендл сессии при успешном logon-е, и он вам нужен будет (обычно) для следующих функций MAPI.

Для использования этой процедуры, я нашел что проще написать для нее wrapper некоторого рода, обычно довольно похожий на следующий: 

LogOn FUNCTION (Prm:UserName,Prm:Password)
LocalVars           Group,Pre(Loc)
ulMapiStatus            ULong
ulMapiSession           ULong
                    End
CODE                                            
Loc:ulMapiStatus = MAPILogon(0,Address(Prm:Username),Address(Prm:Password),MAPI_LOGON_UI + MAPI_NEW_SESSION,0,Loc:ulMapiSession)
IF Loc:ulMapiStatus =  SUCCESS_SUCCESS Then
  Return(Loc:ulMapiSession)
Else
  Return(0)
End

Он имеет эффект "логина" в MAPI. Он всегда создает новую сессию и всегда выводит пользователю диалоговое окно. Если logon успешен, то он возвращает хендл сессии вызывающей процедуре. 

Если вы всегда знаете имя пользователя и пароль, то можно изменить параметр flFlags чтобы не выводилось окно, а просто создавалась бы новая сессия. Но по моему опыту всегда легче (как минимум во время разработки) просто показать диалоговое окно и позволить пользователю его заполнить. В этом случае вы не должны беспокоится о правильности имени пользователя и пароле в параметрах программы. 

Отсылка email

Для отсылки email используется процедура MAPISendMail(). Ее API прототип: 

ULONG FAR PASCAL MAPISendMail( 
LHANDLE lhSession
ULONG ulUIParam
lpMapiMessage lpMessage
FLAGS flFlags
ULONG ulReserved )

Прототип  Clarion:

MAPISendMail(ULONG,ULONG,*GROUP,ULONG,ULONG),ULONG,RAW,PASCAL

Опять, параметры нуждаются в некотором объяснении: 

Важные параметры здесь lpMessage и flFlags. Они определяют как создается emai, что видит пользователь. 

lpMessge это группа следующей структуры:

MapiMessageType     GROUP,TYPE          
ulReserved              ULONG !  Зарезервирован, должен быть 0
lpszSubject             ULONG !  Указатель на subject, CSTRING максимум 256 
lpszNoteText            ULONG !  Указатель на text, CSTRING, CR как параграфы
lpszMessageType         ULONG !  Указатель на message type, null по умолчанию
lpszDateReceived        ULONG !  Указатель на строку в формате YY/MM/DD
lpszConversationID      ULONG !  Игнорируется
flFlags                 ULONG !  1 - MAPI_UNREAD
                              !  2 - MAPI_RECEIPT_REQUESTED
                              !  3 - MAPI_SENT
lpOriginator            ULONG !  Указатель на группу MapiRecipDesc
nRecipCount             ULONG !  Счетчик групп указываемых lpRecips
lpRecips                ULONG !  Указатель на массив групп recipient descriptor
nFileCount              ULONG !  Счетчик групп file attachment указываемых  lpFiles
lpFiles                 ULONG !  Указатель на массив групп file attachment
                    END

При отсылке email, вы должны сначала определить будет ли ваша программа ответственна за приглашения пользователю о subject, destination address и mail text , или же MAPI должно выводить свое собственное окно. Если первое, то вы должны заполнить соответствующие поля группы и вызвать MAPISendMail() не устанавливая флаг MAPI_DIALOG. Если же вы хотите чтобы MAPI делала за вас всю эту работу (а почему бы и нет?), то просто оставьте группу пустой и установите признак MAPI_DIALOG. Это приведет к появлению стандартного окна почты Microsoft, и освободит вас от разработки другого окна. 

Если вы не хотите передать MAPI всю эту работу, то это скорее нудно чем быстро. Destination addresses работает по принципу счетчика (число людей которым посылается почта), и указателя на массив из групп (одна для каждого человека). Вы должны заполнить поля lpszSubject и lpszNoteText адресом (ADDRESS()) соответствующей CSTRING. nRecipCount надо установить в 1, и установить lpRecips адресом (ADDRESS()) структуры содержащей адреса первого человека, которому вы посылаете сообщения. Эта структура выглядит наподобие:

MapiRecipDescType   GROUP,TYPE        ! УКАЗАТЕЛЬ ПОЛУЧАТЕЛЯ
ulReserved              ULONG           !  Зарезервирован, должен быть 0
ulRecipClass            ULONG           !  Роль получателя в сообщении                                        !  0 - MAPI_ORIG
                                        !  1 - MAPI_TO
                                        !  2 - MAPI_CC
                                        !  3 - MAPI_BCC
lpszName                ULONG           !  Указатель на выводимое имя получателя
lpszAddress             ULONG           !  Указатель на почтовый адрес получателя
ulEIDSize               ULONG           !  Размер (в байтах) двоичных данных в lpEntryID.
lpEntryID               ULONG           !  Указатель на двоичные данные представляющие получателя
                    END

Практически, я нашел, что в этой структуре нужно только установить поля ulRecipClass (для MAPI_TO и lpszName как ADDRESS() CSTRING содержащей его имя). Если вам также нудны CC или BCC где либо, то то нужно увеличить счетчик nRecipCount оригинальной группы, и установить ulRecipClass в MAPI_CC или MAPI_BCC в N-ой копии группы. все остальное можно оставить равным 0 или NULL.

Если нужно послать приложения, это также нудно. Приложения описываются таким же самым образом что и адреса назначения - имеется счетчик (число приложений), и указатель на массив групп (по одной на приложение). Группы приложений следующие:

MapiFileDescType GROUP,TYPE ! FILE DESCRIPTOR
ulReserved     ULONG(0) ! Reserved, must be 0.
flFlags            ULONG(0) ! Ignored.
nPosition        ULONG ! where in the message text to place the file
lpszPathName ULONG ! Pointer to full pathname of attached file.
lpszFileName  ULONG ! Pointer to file name seen by user.
lpFileType      ULONG(0) ! Reserved, must be NULL.
END

Как вы видите, виртуально можно оставить все поля пустыми, кроме lpszPathName, nPosition и lpszFileName. Я нашел что хорошая идея не передавать в lpszPathName полные имена и путями, так как будут проблемы на приемном конце если путь не существует. Вместо этого, я пытаюсь использовать PATH() для сохранения текущего пути, делать SETPATH() на путь где лежит файл, и затем делать SETPATH() назад перед вызовом MAPISendMail().

Другая проблема с приложениями - поле nPosition. Это поле определяет где размещен файл приложения, поэтому если вы установите его равным 1, то приложение перекроет первый символ текста. Под конец я добавляю один дополнительный символ в конец текста и затем устанавливаю поле nPosition в позицию этого символа.

Проверка и получение почты

Один из недостатков Simple MAPI это недостаток функций (это потому что называется Simple!)

Этот недостаток функций проявляет себя в том что вы можете проверить наличие почты только в главном приемном каталоге почты (Inbox). Если вы опытный пользователь и используете Inbox Assistant для перенаправления сообщений в другие каталоги, то Simple MAPI не сможет прочитать почту в этих каталогах, только в главном каталоге.

Однако, если это все что вы хотите сделать, то вот как это делается. Но теперь вам потребуется уже три функции:

    ULONG FAR PASCAL MAPIFindNext(  
    LHANDLE lhSession, 
    ULONG ulUIParam, 
    LPTSTR lpszMessageType, 
    LPTSTR lpszSeedMessageID, 
    FLAGS flFlags, 
    ULONG ulReserved, 
    LPTSTR lpszMessageID )

    ULONG FAR PASCAL MAPIReadMail
    LHANDLE lhSession, 
    ULONG ulUIParam, 
    LPTSTR lpszMessageID, 
    FLAGS flFlags, 
    ULONG ulReserved, 
    lpMapiMessage FAR * lppMessage )

ULONG FAR PASCAL MAPIFreeBuffer( LPVOID pv )

Соответствующие прототипы Clarion:

MAPIFindNext(ULONG,ULONG,ULONG,ULONG,ULONG,ULONG,ULONG),ULONG,RAW,PASCAL
MAPIReadMail(ULONG,ULONG,ULONG,ULONG,ULONG,ULONG),ULONG,RAW,PASCAL
MAPIFreeBuffer(ULONG),ULONG,RAW,PASCAL

MAPIFindNext() ищет следующий непрочитанный email, и возвращает идентификатор сообщения. MAPIReadMail() принимает этот идентификатор и действительно получает сообщение (отмечая его как прочитанное). MAPIFreeBuffer() освобождает буфер, который был выделен для почты. Не освобождение буфера приводит к "утескам памяти", которые через некоторое время могут поставить машину на колени. Никогда не забывае освобождать буфер!

Мы начинаем обработку вызовом MAPIFindNext():

MAPI_GUARANTEE_FIFO
Возвращаемые идентификаторы сообщений должны извлекаться в порядке времени. Вызовы MAPIFindNext могут быть дольше если установлен этот флаг. Некоторые реализации не принимают этот запрос и возвращают значение MAPI_E_NO_SUPPORT.
MAPI_LONG_MSGID Возвращаемый идентификатор сообщения может быть длиной 512 символов. Если этот признак установлен, то параметр lpszMessageID должен быть достаточным для приема до 512 символов.

Старые версии MAPI поддерживают малые идентификаторы сообщений (64 байта) и не включают этот флаг. MAPIFindNext успешно завершится без установки этого флага если lpszMessageID достаточно велик для того чтобы вместить идентификатор сообщения. Если lpszMessageID не может вместить идентификатор сообщения, то MAPIFindNext завершиться неудачно.

MAPI_UNREAD_ONLY Должны быть перечислены только непрочитанные сообщения указанного типа. Если этот флаг не установлен, то MAPIFindNext может возвратить любое сообщение указанного типа.

LpszMessageID - возвращается, и является идентификатором первого непрочитанного сообщения.

Придерживаясь идеи написания wrapper-ов вокруг всего, процедура для поиска следующего непрочитанного сообщения может быть примерно такой:

FindNextUnread FUNCTION                  
LocalVars           Group,Pre(Loc)
ulMapiStatus            ULong
MessageType             CString(10)
MessageID               CString(515)
End
  CODE                                            
        Loc:MessageType = ''
        Loc:ulMapiStatus = MapiFindNext(SMAPI:Session,|
                                        0,|
                                        0,|
                                        0,|
                                        MAPI_UNREAD_ONLY+MAPI_LONG_MSGID,|
                                        0,|
                                        Address(Loc:MessageID))
        If Loc:ulMapiStatus <> SUCCESS_SUCCESS Then
            Clear(Loc:MessageID)
        End
Return(Clip(Loc:MessageID))

Когда мы нашли следующее новое сообщение, следующее что нужно сделать - это действительно прочитать сообщение. Параметры для  MAPIReadMail():

MAPI_BODY_AS_FILE MAPIReadMail должна записывать текст сообщения во временный файл и добавить его как первое приложение (attachment) в списке приложений.
MAPI_ENVELOPE_ONLY MAPIReadMail должна читать только заголовок сообщения. Файлы приложений не копируются во временные файлы, и никакие имена временных файлов и тексты сообщения не записывается. Установка этого флага увеличивает быстродействие.
MAPI_PEEK MAPIReadMail не отмечать сообщение как прочитанное. Отметка сообщения как прочитанного действует на его видимость в интерфейсе пользователя и генерирует подтверждение получения. Если система сообщений не поддерживает этот флаг, то MAPIReadMail всегда отмечает сообщение как прочитанное. Если MAPIReadMail встретит ошибку, то она оставляет сообщение напрочитанным.
MAPI_SUPPRESS_ATTACH MAPIReadMail не должна копировать файловые приложения (attachments) но должна записывать текст сообщения в структуру MapiMessage. MAPIReadMail игнорирует этот флаг если вызывающая программа установила флаг MAPI_ENVELOPE_ONLY. Установка признака MAPI_SUPPRESS_ATTACH увеличивает быстродействие.

lppMessage  это указатель на структуру сообщения (это та же самая структура что используется для отправки сообщений). Структура может быть освобождена одним вызовом функции MAPIFreeBuffer(). Если флаги MAPI_ENVELOPE_ONLY и MAPI_SUPPRESS_ATTACH не установлены, то приложения (attachments) записываются во временные файлы, указываемые параметром lpFiles структуры MapiMessage. Ответственность за удаление этих файлов лежит на вызывающей программе если они больше не нужны.

Код будет выглядеть наподобие вот этого:

ReadMail FUNCTION (Prm:MessageID)     
LocalVars           Group,Pre(Loc)
ulMapiStatus            ULong
MessageType             CString(10)
MessageID               CString(100)
MailTitle               CString(255)
MailSender              CString(255)
MailText                CString(8192)
MessagePtrPtr           ULong
MessagePtr              ULong
                    End
RxMessage           Like(MapiMessageType)       ! message info
RxSender            Like(MapiRecipDescType)     ! sender info
RxFile              Like(MapiFileDescType)      ! attached file info

CODE
If SMAPI:LoggedOn Then
  Loc:MessageID = Clip(Prm:MessageID) & Chr(0)
  Clear(RxMessage)
  Loc:MessagePtrPtr = Address(RxMessage)
  Loc:ulMapiStatus = MAPIReadMail( SMAPI:Session,|
                                   0,|
                                   Address(Loc:MessageID),|
                                   MAPI_SUPPRESS_ATTACH,|
                                   0,|
                                   Loc:MessagePtrPtr)
  If Loc:ulMapiStatus = SUCCESS_SUCCESS Then
    MemCpy( Address(Loc:MessagePtr) , Loc:MessagePtrPtr , 4)
    MemCpy( Address(RxMessage) , Loc:MessagePtr , Size(RxMessage) )
    If RxMessage:lpszSubject <> 0 Then
      MemCpy( Address(Loc:MailTitle) , RxMessage:lpszSubject , 100 )
    Else
      Loc:MailTitle = 'Untitled'
    End
    If RxMessage:lpszNoteText <> 0 Then
      memcpy( Address(Loc:MailText) , RxMessage:lpszNoteText , 100 )
    Else
      Clear(Loc:MailText)
    End
    If RxMessage:lpOriginator <> 0 Then
      memcpy( Address(RxSender), RxMessage:lpOriginator, Size(RxSender) )
      If RxSender:lpszAddress <> 0 Then
        memcpy( Address(Loc:MailSender), RxSender:lpszAddress, 100 )
      End
    Else
      Clear(Loc:MailSender)
    End
    SMAPI:Rx:MailTitle = Clip(Loc:MailTitle)
    SMAPI:Rx:MailText = Clip(Loc:MailText)
    SMAPI:Rx:MailSender = Clip(Loc:MailSender)

    Loc:ulMapiStatus = MAPIFreeBuffer(Loc:MessagePtr)
    If Loc:ulMapiStatus <> SUCCESS_SUCCESS Then
      SMAPI:ErrorString = MapiError(Loc:ulMapiStatus)
    End
  End
End
Return(Loc:ulMapiStatus)

Если вы внимательно посмотрите этот код, то увидите что мы продвигаемся от указателя к указателю структуры сообщения. Когда мы знаем что сообщение успешно прочитано, мы вызываем MemCpy для копирования адресов структуры в первый указатель. Мы копируем 4 байта потому что их размер long, что обычно для указателей в Clarion. Как только у нас есть указатель на структуру сообщения, мы de-reference его опять и действительно копируем данные из памяти в структуру сообщения, потому что так проще работать. После того как сообщение прочитано (и "отвечено" если нужно), нам нужно освободить память структуры.

Отключение (Logging off) от MAPI

Следующее. что нам нужно сделать - отключиться (logoff) и закрыть сессию. Прототип API:

ULONG FAR PASCAL MAPILogoff ( 
LHANDLE lhSession, 
ULONG ulUIParam, 
FLAGS flFlags, 
ULONG ulReserved )

Прототип Clarion:

MAPILogoff(ULONG,ULONG,ULONG,ULONG),ULONG,PASCAL,RAW

По сравнению с тем то мы уже сделали с MAPI, реализация этого кода очень легка:

LogOff PROCEDURE
LocalVars           Group,Pre(Loc)
ulMapiStatus            ULong
                    End
CODE                                            
Loc:ulMapiStatus = MAPILogoff( SMAPI:Session,0,0,0)
IF Loc:ulMapiStatus = SUCCESS_SUCCESS then
  SMAPI:LoggedOn = False
End

Выводы

Когда понятны примененные принципы, использование Simple MAPI добавляет поддержку электронной почты в ваши программы довольно легко. Осталось еще довольно много, что я не смог рассказать, просто потому что вопрос довольно сложен, но это должно дать вам возможность начать.

Конечно, по своей природе Simple MAPI имеет ограниченные функции, но даже это дает вам некоторые идеи по ее применению. Если вы не думаете что Simple MAPI достаточно для вашей задачи, то попытайтесь использовать шаблоны третьих фирм или контролы VBX/OCX - я уверен что они имеют нужные вам функции.

В следующий раз …

Использование Windows API для связи через RS232

Загрузить исходный текст примера


На домашнюю страницу. Или послать мне письмо paula@attglobal.net

(c) 1999 Paul Attryde

1