Разработка внешних компонент (ВК) для 1С 7.7 — это мощный инструмент, позволяющий расширить функционал системы за счет низкоуровневого программирования на C++. Однако новички часто сталкиваются с трудностями при попытке «подружить» мир C++ и внутренние объекты 1С, такие как ТаблицаЗначений. Разберем подробно, как организовать это взаимодействие, используя технологию COM и интерфейс IDispatch.
Для того чтобы внешняя компонента могла «общаться» с 1С, она должна использовать механизмы автоматизации COM. Основным инструментом здесь выступает интерфейс IDispatch. Проанализируем ситуацию: когда вы подключаете ВК, 1С передает ей указатель на свое окружение. В методе Init вашей компоненты вы получаете объект pConnection. Это и есть ваш «мостик» в мир 1С.
Многие ошибочно полагают, что передать сложные объекты вроде ТаблицаЗначений в ВК невозможно. На самом деле, хотя C++ не знает внутренней структуры класса 1С, он может работать с ним как с объектом IDispatch. Это означает, что мы можем запрашивать список методов объекта и вызывать их по идентификаторам (DISPID).
Рассмотрим по шагам процесс вызова метода объекта 1С из кода на C++. Допустим, у нас есть объект ТаблицаЗначений, и мы хотим вызвать её метод НоваяСтрока. Посмотрим на алгоритм:
GetIDsOfNames. Мы передаем ей строку "НоваяСтрока", а она возвращает нам DISPID.VARIANT, упакованных в структуру DISPPARAMS. Важно помнить: в COM параметры в массиве располагаются в обратном порядке.Invoke, куда передается полученный ранее DISPID, параметры и контекст выполнения.Пример реализации вызова метода на C++ может выглядеть следующим образом:
HRESULT Call1CMethod(IDispatch* pDisp, LPOLESTR methodName) {
DISPID dispid;
// 1. Получаем ID метода по его имени
HRESULT hr = pDisp->GetIDsOfNames(IID_NULL, &methodName, 1, LOCALE_USER_DEFAULT, &dispid);
if (FAILED(hr)) return hr;
// 2. Подготавливаем пустые параметры (если метод их не требует)
DISPPARAMS params = { NULL, NULL, 0, 0 };
VARIANT result;
VariantInit(&result);
EXCEPINFO excep;
UINT argErr;
// 3. Вызываем метод
hr = pDisp->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &result, &excep, &argErr);
VariantClear(&result);
return hr;
}
Рассмотрим проблему передачи данных. Существует два основных подхода к решению задачи, когда нужно вернуть результат сложной выборки (например, из ADO) обратно в 1С.
Вариант 1: Заполнение таблицы «на лету»
Вместо того чтобы пытаться «создать» таблицу в C++ и вернуть её, гораздо эффективнее создать пустую ТаблицаЗначений в 1С и передать её в метод вашей ВК как параметр. Внутри C++ кода вы получите её как VARIANT с типом VT_DISPATCH.
Выясним причину, почему это лучше: вам не нужно заботиться о выделении памяти под объект 1С, вы просто используете методы этого объекта (НоваяСтрока, УстановитьЗначение), чтобы наполнить его данными из вашего Recordset ADO. Это избавляет от проблем с типами данных и преобразованием строк.
Вариант 2: Сериализация через ЗначениеВСтроку
Если работа с IDispatch кажется слишком громоздкой, можно воспользоваться встроенными функциями 1С ЗначениеВСтрокуВнутр и ЗначениеИзСтрокиВнутр. Проанализируем этот путь: вы можете передать в ВК строку, представляющую объект, или же ВК может сформировать строку в специальном внутреннем формате 1С и вернуть её. Однако помните, что для больших таблиц этот метод может быть медленным и потреблять много оперативной памяти.
Иногда требуется создать объект 1С (например, ту же таблицу) непосредственно внутри ВК. Для этого мы можем обратиться к глобальному контексту 1С через переданный при инициализации указатель pConnection. Мы можем вызвать у него метод AppDispatch (или аналогичный в зависимости от реализации интерфейса), чтобы получить доступ к функции СоздатьОбъект.
Разберем пример логики:
pConnection вызываем IDispatch::Invoke для функции EvalExpr (ВычислитьВыражение)."СоздатьОбъект(\"ТаблицаЗначений\")".Invoke получаем VARIANT, содержащий указатель IDispatch на новую таблицу.При использовании современных версий Visual Studio настоятельно рекомендуем использовать умные указатели ATL, такие как CComPtr<IDispatch> или CComVariant. Это значительно упрощает управление жизненным циклом объектов и предотвращает утечки памяти, которые в 1С 7.7 могут привести к падению системы при частом вызове ВК.
Особое внимание уделите типам данных:
BSTR. Используйте SysAllocString для создания строк и следите за их освобождением.VT_R8 (double) или VT_I4 (long).DATE (число с плавающей точкой), который совпадает с форматом DATE в COM.HRESULT и заполняйте структуру EXCEPINFO, чтобы 1С могла корректно отобразить описание ошибки, если что-то пойдет не так в коде компоненты.Если ваш запрос через ADO занимает значительное время, выполнение метода ВК заблокирует интерфейс 1С. В этом случае рассмотрим использование интерфейса IAsyncEvent. Вы можете запустить выборку данных в отдельном потоке внутри вашей DLL, а по завершении вызвать метод ExternalEvent. 1С получит уведомление в процедуре ПриВнешнемСобытии, где вы сможете передать, например, путь к сформированному файлу или идентификатор кэшированных данных.
Таким образом, взаимодействие между 1С 7.7 и внешней компонентой на C++ строится на стандартных принципах COM. Использование прямого управления объектами через IDispatch::Invoke является наиболее гибким и производительным способом передачи данных, превосходящим по эффективности передачу строк или простых массивов.