При интеграции внешних приложений на языке C++ с платформой 1С:Предприятие часто используется компонент COMConnector (например, V82.COMConnector или V83.COMConnector). Однако при низкоуровневой работе с интерфейсом IDispatch разработчики нередко сталкиваются с ошибкой 0x80020008 (DISP_E_BADVARTYPE — «Один из аргументов метода не является допустимым типом variant») при попытке прочитать свойство или вызвать метод объекта (например, получить Метаданные или Версия).
Разберем по шагам, почему возникает эта ошибка и как корректно вызывать свойства и методы объектов 1С через COM в Visual C++.
Проанализируем механику работы вызова метода IDispatch::Invoke. При получении свойства через флаг DISPATCH_PROPERTYGET или вызове метода через DISPATCH_METHOD вызывающая сторона передает указатель на структуру VARIANT, куда сервер автоматизации должен записать результат (параметр pVarResult).
Согласно стандартам COM/OLE Automation, реализация метода внутри COM-сервера 1С перед записью нового результата в выходной VARIANT вызывает стандартную системную функцию VariantClear. Это необходимо для того, чтобы освободить предыдущие ресурсы, если в переменной уже хранились динамические данные (например, интерфейсные указатели VT_DISPATCH или строки VT_BSTR).
Если локальная переменная VARIANT была объявлена на стеке без предварительного обнуления, поле vt содержит случайный «мусор» из оперативной памяти. Функция VariantClear обнаруживает некорректный идентификатор типа и возвращает ошибку DISP_E_BADVARTYPE (код 0x80020008), прерывая выполнение вызова Invoke.
Выясним, как устранить ошибку. Для этого достаточно инициализировать выходной VARIANT перед каждым вызовом Invoke.
Рассмотрим последовательность действий:
vRetMeta.vt = VT_EMPTY; (или vRetMeta.vt = 0;).VariantInit(&vRetMeta);.VariantClear(&vRetMeta); для корректного освобождения памяти и уменьшения счетчика ссылок COM-объекта.Посмотрим на пример исправленного блока кода обращения к свойству Метаданные:
VARIANT vRetMeta;
VariantInit(&vRetMeta); // Обязательная инициализация перед вызовом!
DISPPARAMS argsMeta = { NULL, NULL, 0, 0 }; // Для получения свойства без параметров
DISPID dispID;
BSTR MethodName = L"Метаданные";
hr = connector->GetIDsOfNames(IID_NULL, &MethodName, 1, 0, &dispID);
if (SUCCEEDED(hr))
{
EXCEPINFO exs;
hr = connector->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &argsMeta, &vRetMeta, &exs, NULL);
if (SUCCEEDED(hr) && vRetMeta.vt == VT_DISPATCH)
{
IDispatch* pMetadata = vRetMeta.pdispVal;
std::cout << "Интерфейс IDispatch метаданных успешно получен" << std::endl;
// Дальнейшая работа с объектом метаданных...
// Освобождаем полученный IDispatch
pMetadata->Release();
}
}
VariantClear(&vRetMeta);
Ручной вызов методов через IDispatch::Invoke, ручное заполнение DISPPARAMS и управление счетчиками ссылок через AddRef/Release трудоемки и подвержены утечкам памяти. Рассмотрим более надежные и современные подходы:
#import: Visual C++ позволяет импортировать библиотеку типов 1С (comcntr.dll). Компилятор автоматически сгенерирует строго типизированные C++ классы-обертки (_com_ptr_t, _variant_t, _bstr_t), благодаря чему обращение к базе 1С станет лаконичным:
#import "C:\Program Files\1cv8\8.3.XX.XXXX\bin\comcntr.dll" rename("EOF", "V8EOF")
try {
V83::IConnectorPtr pConnector(__uuidof(V83::COMConnector));
IDispatchPtr pConnection = pConnector->Connect(L"File=\"D:\\База1С\";Usr=\"Admin\";");
// Прямое чтение свойств через высокоуровневые обертки
_variant_t vMetadata = pConnection->GetProperty(L"Метаданные");
// Обработка данных...
}
catch (_com_error& e) {
std::wcout << L"Ошибка COM: " << e.ErrorMessage() << std::endl;
}
CComPtr, CComVariant и CComDispatchDriver. Метод CComDispatchDriver::GetPropertyByName берет на себя вызовы GetIDsOfNames, инициализацию структур и вызов Invoke.comcntr.dll является внутрипроцессным COM-сервером (in-process). 32-битное приложение C++ должно использовать 32-битный COM-коннектор 1С, а 64-битное приложение — 64-битный. Если разрядности различаются, создание объекта завершится ошибкой, либо потребуется настройка выделенного COM+-приложения в службе компонентов Windows.