Страница 1 из 2 В данной статье показано, как можно работать с 1С Предприятием из С++ с помощью OLE DB. Так же она будет интересна тем, кто не пользуется C++, но хочет узнать подробности "а как оно устроено внутри 1С". В данной статье речь пойдет об 1С Предприятии версии 7.7. Полагаю, что в версии 8 мало что изменилось. Предполагается, что читатель хотя бы чуть-чуть знаком с 1С Предприятием. Так же предполагается, что вы изучали официальное руководство 1С по вопросам OLE DB (часть вторая описания языка), но осталься недоволен - т.к. исходники там приведены для Visual Basic, а вам ну очень нужен именно C++ (кстати - не обязательно Visual - главное что бы в вашем компиляторе была возможность работать с OLE).
1С Предприятие предоставляем пользователям механизм OLE DB. Если Вам вдруг захотелось использовать какие либо данные из 1С Предприятия в вашей программе – вы можете воспользоваться этим механизмом. Совсем просто это делается в таких языках, как Visual Basic или Delphi. В них вся работа с OLE-интерфейсами замаскирована от программиста насколько возможно. Это, с одной стороны, очень удобно, но с другой стороны – у нас всегда есть возможность повысить производительность путём использования С++. Он от своих адептов ничего не маскирует, это позволяет кое где сэкономить лишний байт или миллисекунду, но превращает работу с OLE DB в ад. Дело в том, что 1С не предоставляет для своих пользователей библиотеки импорта (*.tlb), поэтому единственный способ работы (если не использовать какие либо обёртки) – это позднее связывание. В Сети полно информации, как работать с 1С Предприятием из Delphi или Visual Basic, но практически совсем нет примеров с Visual C++.
Эта статья призвана заполнить этот пробел. Здесь Вы найдете кусочки рабочего кода, который Вы можете дописывать и модифицировать по своему усмотрению. По себе знаю, что порою достаточно подсмотреть одним глазком, что бы стало ясно, что к чему. Кроме того, я расскажу про некоторые «тонкие» моменты работы с 1С, которые мягко обойдены в официальных изданиях, имеющих красивый красно-желтый цвет.
Наиболее разумным и удобным будет следующее решение: мы организуем всю необходимую работу с 1С в виде отдельной экспортируемой функции глобального модуля (я надеюсь, Вы уже научились открывать в 1С Глобальный модуль и добавлять в него экспортируемые функции). Мы будем вызывать нужную функцию глобального модуля, которая будет возвращать нужное значение. Для многих практических приложений этого вполне достаточно.
#include <objbase.h> #include <comdef.h>
//для начала инициализируем COM HRESULT hr = CoInitialize(NULL); if(FAILED(hr)) { AfxMessageBox("Невозможно инициализировать COM!"); return FALSE; }
/* Прежде всего, нам необходимо получить ID сервера OLE Automation 1С Предприятия. */ CLSID cls77;
/* Используем универсальный ключ 1С Предприятия
Подробнее см. КЖК – если у Вас установлена единственная версия 1С – то этого достаточно, если несколько разных, то нужно загрузить нужный. Вот краткий список возможных значений:
V1CEnterprise.Application - версия независимый ключ; V77.Application - версия зависимый ключ; V77S.Application - версия зависимый ключ, SQL-версия; V77L.Application - версия зависимый ключ, локальная-версия; V77M.Application - версия зависимый ключ, сетевая-версия. */
hr = CLSIDFromProgID(L"V77.Application", &cls77); if(FAILED(hr)) { AfxMessageBox("Переустановите 1С Предприятие!"); CoUninitialize(); return FALSE; }
//основной интерфейс, за который мы будем "дёргать" IDispatch *pv77;
/* Создаём инстанцию 1С Предприятия.
CLSCTX_LOCAL_SERVER – это значит, что 1С Предприятие будет запущено в виде отдельного процесса – по другому оно не умеет. */
hr = CoCreateInstance(cls77, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void**)&pv77);
if(FAILED(hr) || !pv77) { AfxMessageBox("Невозможно инициализировать интерфейс 1С Предприятия"); CoUninitialize(); return FALSE; }
/* пока всё было понятно и очевидно, дальше начинаются сложности… 1С предоставляет для запуска приложения функцию Initialize. Вызов этой функции выглядит в VB элементарно
V7.Initialize(V7.RMTrade,"D:\1C\ТипаБаза /N"+Пользователь ,"NO_SPLASH_SHOW");
- мы практически забываем, что же происходит внутри. Но занимающиеся С++ люди хладнокровные, трудностей не боятся. Во-первых: мы должны помнить, что аргументы необходимо заталкивать задом наперёд… Во-вторых: мы должны помнить, что RMTrade - это доже IDispatch интерфейс.. и его сперва нужно получить. */
VARIANT vRet; DISPID dispIDRmTrade, dispIDInitialize; DISPPARAMS args = {0, 0, 0, 0}; VARIANT vars[3]; // Параметры для вызова Initialize
//Мы получим IDispatch интерфейс от RMTrade сразу в vars[2]
BSTR rmTrade = L"RMTrade";
hr = pv77->GetIDsOfNames(IID_NULL, &rmTrade, 1, 0, &dispIDRmTrade);
if (FAILED(hr)) { AfxMessageBox("Невозможно получить ID от RMTrade"); if (pv77) pv77->Release(); CoUninitialize(); return FALSE; }
hr = pv77->Invoke(dispIDRmTrade, IID_NULL, 0, DISPATCH_PROPERTYGET, &args, &vars[2], NULL, NULL);
if (FAILED(hr)) { AfxMessageBox("Невозможно получить интерфейс от RMTrade"); if (pv77) pv77->Release(); CoUninitialize(); return FALSE; }
//нужно получить ID для Initialize(..); BSTR init = L"Initialize"; hr = pv77->GetIDsOfNames(IID_NULL, &init, 1, 0, &dispIDInitialize); if (FAILED(hr)) { AfxMessageBox ("Не удалось получить ID от Initialize") if (pv77) pv77->Release(); CoUninitialize(); return FALSE; }
/* а теперь – вызвать этот самый Initialize(..), но сперва необходимо заполнить массив аргументов функции */
args.cArgs = 3; args.rgvarg = vars; vars[0] = _variant_t("NO_SPLASH_SHOW"); vars[1] = _variant_t("/D D:\1S /N Denis /P Denis ");
/* vars[2] – у нас уже есть, мы его получили при запросе интерфейса RMTrade в момент предыдущего Invoke */
hr = pv77->Invoke(dispIDInitialize, IID_NULL, 0, DISPATCH_PROPERTYGET, &args, &vRet, NULL, NULL);
if(FAILED(hr) || (vRet.vt == VT_BOOL && vRet.bstrVal == 0x00)) { AfxMessageBox("Невозможно запустить 1С Предприятие"); }
| Надо заметить, что с этим последним Invoke нужно быть вообще осторожнее. У меня было такое, что даже при правильной командной строке вызов не удавался. При этом должно появиться окно, предлагающее задать имя базы, имя пользователя и т.п., возможно, это такая особенность дизайна 1С. В конце концов пришлось удалить старого пользователя, и создать нового. Осторожнее при разработке сервисов! Это окно будет появляеться на виртуальном десктопе, и Вы просто ничего не увидите (если конечно, не поставите галочку «взаимодействовать с рабочим столом»).
|