В процессе интеграции различных систем с 1С часто возникает задача использовать COM-соединение. Стандартный подход требует регистрации библиотеки comcntr.dll в операционной системе. Однако бывают ситуации, когда такая регистрация нежелательна или невозможна: например, из-за отсутствия прав администратора, необходимости работать с несколькими версиями платформы одновременно или при развертывании "portable" приложений.
При попытке создать COM-объект без регистрации, как правило, возникает ошибка. Проанализируем типичное сообщение об ошибке:
Внешнее исключение (System.Runtime.InteropServices.COMException): Не удалось получить фабрику класса COM для компонента с CLSID {181E893D-73A4-4722-B61D-D604B3D67D47} из-за следующей ошибки: 80004005 Неопознанная ошибка (Исключение из HRESULT: 0x80004005 (E_FAIL)).
Эта ошибка означает, что операционная система не смогла найти в реестре информацию о том, какая DLL-библиотека отвечает за создание объекта с указанным идентификатором (CLSID). В этой статье мы подробно разберем, как обойти это ограничение и создать COM-объект 1С, не регистрируя библиотеку в системе.
Прежде чем перейти к основному решению, давайте кратко рассмотрим стандартный путь. Обычно при установке платформы "1С:Предприятие" библиотека comcntr.dll регистрируется автоматически. Если этого не произошло, можно сделать это вручную с помощью системной утилиты regsvr32.exe, запущенной от имени администратора.
Команда выглядит так:
regsvr32 "C:\Program Files\1cv8\[версия платформы]\bin\comcntr.dll"
При выполнении этой команды в системный реестр Windows добавляются записи, которые связывают CLSID объекта V83.COMConnector с путем к файлу comcntr.dll. После этого любая программа может легко создавать COM-объекты, обращаясь к операционной системе.
Теперь разберем основной метод, который позволяет обойтись без регистрации. Идея заключается в том, чтобы вручную выполнить все те действия, которые операционная система обычно делает автоматически, опираясь на данные из реестра. Мы будем использовать функции WinAPI для прямой загрузки DLL и создания объекта. Этот подход известен как "бесрегистрационный COM" (Registration-Free COM).
Рассмотрим процесс по шагам.
Указание пути к зависимым библиотекам. Библиотека comcntr.dll не работает в вакууме. Для ее корректной работы требуется множество других DLL из той же папки bin установленной платформы 1С. Если просто скопировать comcntr.dll в другое место, она не сможет загрузить свои зависимости. Чтобы решить эту проблему, нужно явно указать системе, где искать эти файлы. Для этого используется функция WinAPI AddDllDirectory, которой передается путь к каталогу bin.
Загрузка библиотеки в память. Далее мы загружаем саму библиотеку comcntr.dll в адресное пространство нашего приложения. Для этого можно использовать функцию CoLoadLibrary или LoadLibrary.
Получение "фабрики классов". По стандарту COM, каждая внутрипроцессная DLL-библиотека должна экспортировать функцию DllGetClassObject. Эта функция является точкой входа для создания объектов. Мы получаем ее адрес с помощью GetProcAddress и вызываем, передав ей CLSID нужного нам объекта ({181E893D-73A4-4722-B61D-D604B3D67D47} для V83.COMConnector) и идентификатор интерфейса фабрики классов (IClassFactory). В ответ мы получаем указатель на объект-"фабрику", который умеет создавать экземпляры нашего COM-объекта.
Создание экземпляра объекта. Получив фабрику, мы вызываем ее метод CreateInstance. Этот метод и создает непосредственный экземпляр COM-объекта V83.COMConnector и возвращает нам указатель на его базовый интерфейс IUnknown.
Получение нужного интерфейса для работы. Интерфейс IUnknown является базовым для всех COM-объектов, но он не позволяет вызывать методы, такие как Connect. Чтобы получить доступ к ним, нужно запросить более функциональный интерфейс — IDispatch. Это делается с помощью метода QueryInterface. Интерфейс IDispatch позволяет работать с объектом в режиме позднего связывания (late binding), что идеально подходит для скриптовых языков и сред, где типы определяются во время выполнения.
После выполнения этих шагов мы получаем полнофункциональный COM-объект, готовый к работе, как если бы он был создан стандартным способом.
Давайте проанализируем пример кода на языке Delphi, который наглядно демонстрирует описанный выше алгоритм. Этот код выполняет все необходимые шаги для создания COM-объекта без регистрации.
function FixinMagic(): TResult;
var
FullFilePath, FileDir: String;
FileName: String;
NewID: TGUID;
LibHandle: THandle;
AddDllDirectory: TAddDllDirectory;
DllGetClassObject: TDllGetClassObject;
res: THandle;
Factory: IClassFactory;
unk: IUnknown;
Obj: IDispatch;
hkernel32: THandle;
ErrorCode: Cardinal;
ErrorDescription: String;
begin
Result.Result := false;
// Указываем полный путь к comcntr.dll
FullFilePath := 'C:\Program Files\1cv8\8.3.25.1336\bin\comcntr.dll';
FileDir := ExtractFilePath(FullFilePath);
// CLSID для V83.COMConnector
NewID := StringToGUID('{181E893D-73A4-4722-B61D-D604B3D67D47}');
// Получаем доступ к функции AddDllDirectory из kernel32.dll
hkernel32 := GetModuleHandle(Pchar('kernel32.dll'));
if hkernel32 = 0 then
hkernel32 := LoadLibrary(Pchar('kernel32.dll'));
// ... (проверка ошибок) ...
@AddDllDirectory := GetProcAddress(hkernel32, 'AddDllDirectory');
// Шаг 1: Добавляем каталог с зависимостями в пути поиска
res := AddDllDirectory(Pchar(FileDir));
// ... (проверка ошибок) ...
// Шаг 2: Загружаем библиотеку
LibHandle := CoLoadLibrary(Pchar(FullFilePath),true);
// ... (проверка ошибок) ...
// Получаем адрес функции DllGetClassObject
@DllGetClassObject := GetProcAddress(LibHandle, 'DllGetClassObject');
// ... (проверка ошибок) ...
// Шаг 3: Получаем фабрику классов
res := DllGetClassObject(NewID, IClassFactory, Factory);
// ... (проверка ошибок) ...
// Шаг 4: Создаем экземпляр объекта
res := Factory.CreateInstance(nil, IUnknown, unk);
// ... (проверка ошибок) ...
// Шаг 5: Получаем интерфейс IDispatch
res := unk.QueryInterface(IDispatch, Obj);
// ... (проверка ошибок) ...
// Возвращаем готовый объект
Result.Data := Obj;
Result.Result := true;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
MagicResult: TResult;
Connector1C, Connection1C: OleVariant;
begin
MagicResult := FixinMagic();
if MagicResult.Result = false then
begin
ShowMessage(MagicResult.ErrorDescription);
exit;
end;
// Используем полученный объект
Connector1C := MagicResult.Data;
try
Connection1C := OleVariant(Connector1C).Connect('Srvr="Localhost";Ref="Empty"');
ShowMessage(Connection1C.Метаданные.Версия);
finally
end;
end;
Как мы видим, после вызова функции FixinMagic мы получаем в переменной Connector1C рабочий COM-объект и можем вызывать его методы, например, Connect, для подключения к информационной базе.
Рассмотренный метод — не единственный способ работы с COM без регистрации. Рассмотрим другие варианты и важные моменты.
Манифесты приложений (Registration-Free COM)
Более современным и предпочтительным способом является использование манифестов приложений. Это специальные XML-файлы, которые поставляются вместе с исполняемым файлом программы. В манифесте декларативно описываются все используемые COM-компоненты, их CLSID и пути к DLL. При запуске такого приложения операционная система создает для него изолированный контекст активации, используя информацию из манифеста вместо глобального системного реестра. Этот подход чище и не требует написания низкоуровневого кода.
Веб-сервисы (HTTP)
Хорошей альтернативой COM-взаимодействию является использование веб-сервисов. Этот подход более универсален, не зависит от платформы и операционной системы (клиент может быть написан на любом языке и работать под Linux или macOS). Он избавляет от сложностей, связанных с регистрацией, версиями и разрядностью COM-компонентов, но требует публикации веб-сервисов на стороне 1С.
Ключевые моменты, на которые стоит обратить внимание
comcntr.dll. Нельзя загрузить 32-битную DLL в 64-битный процесс и наоборот. Убедитесь, что вы используете соответствующую версию платформы 1С.
comcntr.dll не сработает. Необходимо либо запускать приложение из папки bin платформы 1С, либо обеспечить доступ ко всем зависимым библиотекам, как это было показано с помощью AddDllDirectory.
comcntr.dll от конкретной, нужной вам версии платформы 1С, даже если в системе глобально зарегистрирована другая версия. Это может быть полезно для задач тестирования или для интеграционных решений, жестко привязанных к определенной версии 1С.