Насколько мне известно, многие 1С-ники хотели бы изучить написание
внешних компонент, чтобы поднять свое магическое искусство 1С на
качественно иную ступень. Что этому может помешать? Во-первых,
известный синдром компонентофобии (который исторически берет свое
начало от криво написанных внешних компонент). Во-вторых –синдром
клинически запутанного кода. OLE-программирование – это не самая
простая штука, и, как говорится, «не всякая птица долетит до середины
Днепра» (особенно, если эта «птица» – программист 1С). Я предлагаю
вашему вниманию шаблон внешней компоненты, который, как я надеюсь,
достаточно прост для понимания (я постарался его значительно упростить
по сравнению с типовым примером из «Технологии создания внешних
компонент») и стабилен (везде, где это возможно, я использую обработку
исключительных ситуаций).
Сборка проекта
Для компиляции примера
потребуется среда разработки Delphi 6 или 7.
Файл проекта
- TestVK.dpr.
Откройте этот файл (например, двойным
щелчком мыши из Проводника). Нажмите сочетание клавиш Ctrl-F9 (или пункт
меню Project-Compile). Если все прошло нормально, в этой же папке
образуется готовая внешняя компонента TestVK.dll (для проверки, а все
ли хорошо, ее можно удалить, и получить готовую TestVK.dll еще раз).
При
компиляции должна быть закрыта программа 1С в режиме Предприятие –
иначе файл DLL будет заблокирован системой, и Delphi напишут пугающее
сообщение: [Fatal Error] Could not create output file 'TestVK.dll'
Проверка
работоспособности DLL
В комплект примера входит тестовая
конфигурация 1С:Предприятие 7.7. Если компонента зарегистрировалась
нормально (возможно, потребуется вход в Windows и первый запуск под
правами администратора), то откроется отчет, в котором доступны кнопки
"Сообщение", "Предупреждение" и "Сигнал" (я реализовал три метода для
вывода информации из внешней компоненты - знать, как работают подобные
вещи, часто бывает полезно для отладки).
Переименование DLL
Первое,
что я делаю при создании новой внешней компоненты – переименовываю уже
существующий образец. Переименуйте TestVK.dpr так, как вы хотите
(например, MyVK.dpr). Произведите замену всех вхождений подстроки TestVK
в файлах проекта на нужное вам имя внешней компоненты.
Подсказка:
чтобы открыть другие модули проекта, используйте пункт меню View-Units…
Программный
код 1С, разумеется, также нужно не забыть изменить так, чтобы заменить
все подстроки «TestVK».
Важно: замените значение
CLSID внешней компоненты, чтобы новая DLL, с точки зрения Windows,
стала действительно новой.
Чтобы
сгенерировать новый CLSID, нажмите сочетание клавиш Ctrl-Shift-G.
Попробуйте скомпилировать новый проект – 1С должна «увидеть» вашу новую
внешнюю компоненту, которая создана на основе другой ВК, но содержит
полный набор ее свойств и методов.
Что такое свойства и методы?
Новички
могут задаться вопросом, а что такое свойства и что такое методы? В
коде 1С свойства выглядят как, своего рода, «переменные», объекта,
доступные через точку, например
vk.Заголовок="Внешняя компоннета";
Здесь
объект – имеет имя vk (посмотрите, как он объявляется и
инициализируется в глобальном модуле 1С). Этот объект поддерживает
свойства и методы.
В этом коде я установил свойству "Заголовок"
текстовое значение (посмотрите, как будет работать пример, если
установить этому свойству другое значение заголовка, например, «Здесь
был romix», или не устанавливать его вовсе).
Метод объекта – это,
своего рода, «функция» объекта, доступная «через точку».
Метод
может иметь параметры. В данном примере, параметры – это «Проверка
всплывающего сообщения» и 3000 – попробуйте установить в коде 1С
что-нибудь другое и нажать кнопку «Сообщение» в тестовом отчете.
Изменение
списка свойств и методов ВК
В модуле AddinObj.pas за количество
свойств отвечают участки кода, которые я пометил (*2*), (*5*), (*6*),
(*8*), (*11*) а за количество методов - (*3*), (*7*), (*9*), (*10*),
(*12*). Я завел в шаблон по 5 свойств и методов, но что нужно сделать,
чтобы их стало 6, например, в приведенном ниже фрагменте кода, - я
надеюсь, понятно без объяснений.
Для свойств вы увидите примерно такой код,
продублированный, с небольшими отличиями, несколько раз: Это самая
важная часть наших действий, которую важно постараться понять.
///////////////////////////////////////////////////////////////////// function T_vk_object.prop1(mode: TMode): String; begin case mode of m_rus_name: Result:='Пиктограмма'; m_eng_name: Result:='IconType'; m_get_value: g_Value:=g_IconType; m_set_value: g_IconType:=g_Value; end;//case end;
Что
я здесь делаю?
# m_rus_name – устанавливаю
русское имя свойства (в данном случае, свойство называется Пиктограмма).
В коде 1С я пишу что-то вроде
vk.Пиктограмма=32;
Вот
здесь 1С и узнает, что свойство называется именно Пиктограмма (а не
как-то иначе). Попробуйте переименовать свойство (например, в
Псиграмма), и посмотрите, что получится.
#
m_get_value –1С получает значение переменной, в данном случае,
g_IconType.
Что это за переменная? А это та переменная (точнее,
свойство класса), где я решил запоминать идентификатор своей
пиктограммы.
# m_set_value – 1С устанавливает
значение свойства. Наличие этой строки позволяет изменять числовое
значение моей пиктограммы из кода 1С. Попробуйте выставлять из кода 1С
различные значения пиктограммы (в комментариях примера я описал, какие
значения возможны), и смотреть, что получится.
Программирование
функциональности методов
Для методов код похожий:
///////////////////////////////////////////////////////////////////// function T_vk_object.meth1(mode: TMode): String; var s: String; var ms: Integer; begin case mode of m_rus_name: Result:='ВсплывающаяПодсказка'; m_eng_name: Result:='BalloonTooltip'; m_n_params: g_NParams:=2; //Количество параметров функции m_execute: begin //Извлекаем параметры функции, переданные из 1С s:=GetParamAsString(0);//сообщение ms:=GetParamAsInteger(1);//задержка в мс //Показываем сообщение в трее sleep_icon(s, ms); end; end;//case end;
Этот
абзац можно воспроизвести несколько раз (что и сделано в примере),
заменив meth1 на meth2, meth3 и т.д. Приведенные ниже строчки
устанавливают русское и английское имя метода.
Попробуйте
изменить то или другое, перекомпилировать проект и посмотреть, что
получится. В строчке
m_n_params: g_NParams:=2; //Количество параметров функции
я
устанавливаю количество параметров метода. Попробуйте изменять это
количество (например, установить значение 3) и посмотреть, что
получится. Блок
m_execute: begin //… end;
реализует
собственно функциональность метода (показывает всплывающее сообщение в
трее). Подробнее код я опишу ниже – но сначала полезно потренироваться
со вставкой в него отладочной печати (именно так я исследую код).
Отладочная
печать
Вы можете в качестве теста вписать в функциональность
метода что-то свое, например,
MessageBox(0, 'Превед', 'Медвед', 0);
Этот
вызов покажет стандартное окно предупреждения с кнопкой ОК. Или вот
так:
ShowMessage('Просто текст');
Во втором случае,
если Delphi будет ругаться при компиляции, добавьте Dialogs в раздел
uses модуля.
Получение параметров функции
В своей
функции я завел две переменные для хранения параметров:
var s: String; var ms: Integer;
Они
должны хранить, соответственно, текст сообщения (то, что я передаю из
1С) и значение паузы в миллисекундах, в течение которой будет
высвечиваться сообщение в трее. Сейчас мы их заполним значениями,
полученными из 1С.
s:=GetParamAsString(0);//сообщение ms:=GetParamAsInteger(1);//задержка в мс
Параметры
нумеруются, начиная с 0 (давняя традиция программистов на языке С). Чем
отличаются AsString и AsInteger, надеюсь, понятно. Этот код можно было
бы написать и так:
s:= GetNParam(0);//сообщение ms:= GetNParam(1);//задержка в мс
Но
в случае передачи значений неправильного типа (попытайтесь передавать
вместо правильных значений различную "чепуху") сообщения об ошибке будут
менее информативными.
Отладочная печать параметров
При
отладке полученные из 1С значения полезно выводить на экран. Но как это
сделать? Я обычно использую следующий прием. Чтобы показать строковые
переменные, я пишу так:
MessageBox(0, pchar(s), 'Отладка - s', 0);
А
числовые значения и значение типа "дата-время" я отображаю примерно
так:
ShowMessage('Отладка - ms'+#13+IntToStr(ms)); ShowMessage('Время = <'+TimeToStr(Now())+'>; Температура = <'+FloatToStr(RoundTo(t,-2))+'>');
Функциональность
методов
Получив все значения из 1С, можно приступать к
собственно написанию полезного кода.
Функция sleep_icon(s, ms);
определена в коде примера, и показывает сообщение, используя функции
Windows API (Application Programming Interface).