Native api 1c python

Native API компонента для использования установленного в системе интерпретатора Python

Компонента позволяет подключать к платформе 1С python-модули, оформленные в стиле внешней компоненты. Кроме этого, она позволяет запускать небольшие фрагменты python-кода прямо из модулей 1С. На данный момент поддерживаются платформы 8.2 — 8.3 x86 Windows.

В системе должен присутствовать совместимый интерпретатор Python. В данном случае это Python 3.3, инсталлятор которого можно скачать на официальном сайте разработчиков: http://www.python.org/ftp/python/3.3.5/python-3.3.5.msi Интерфейс взаимодействия позволяет оформлять модули расширения максимально просто и наглядно. Для примера, вот функциональный аналог образца Native ВК от ИТС:

# -*- coding: utf-8 -*- from interfacing import * from time import sleep, monotonic from threading import Thread class Timer(Thread): def __init__(self, ent): self.done = False self.ent = ent Thread.__init__(self) def run(self): while not self.done: self.ent.ExternalEvent('ComponentNative', 'Timer', str(monotonic())) sleep(5) self.done = False @Component class CAddInNative: def __init__(self): self.m_boolEnabled = False self.AsyncEvent = Timer(self.Enterprise) @Readable('IsEnabled', 'Включен') @property def ePropIsEnabled(self): return self.m_boolEnabled @Writable @ePropIsEnabled.setter def ePropIsEnabled(self, value): self.m_boolEnabled = value @Readable('IsTimerPresent', 'ЕстьТаймер') @property def ePropIsTimerPresent(self): return True @Procedure('Enable', 'Включить') def eMethEnable(self): self.m_boolEnabled = True @Procedure('Disable', 'Выключить') def eMethDisable(self): self.m_boolEnabled = False @Procedure('ShowInStatusLine', 'ПоказатьВСтрокеСтатуса') def eMethShowInStatusLine(self, var): self.Enterprise.SetStatusLine(var) sleep(5) @Procedure('StartTimer', 'СтартТаймер') def eMethStartTimer(self): self.AsyncEvent.start() @Procedure('StopTimer', 'СтопТаймер') def eMethStopTimer(self): self.AsyncEvent.done = True @Function('LoadPicture', 'ЗагрузитьКартинку') def eMethLoadPicture(self, name): with open(name, 'rb') as f: return f.read() @Procedure('ShowMessageBox', 'ПоказатьСообщение') def eMethShowMsgBox(self): info = self.Enterprise.GetInterface(EC_IPlatformInfo) imsgbox = self.Enterprise.GetInterface(EC_IMsgBox) if info is None or imsgbox is None: return if imsgbox.Confirm(info.GetPlatformInfo()[0]): imsgbox.Alert('OK') else: imsgbox.Alert('Cancel') def ExplicitDone(self): self.AsyncEvent.done = True self.AsyncEvent.join()

Тестовая конфигурация

В архиве есть достаточно подробная документация и комплект для тестирования. Чтобы запустить тест, нужно указать путь к компоненте, по которому должен лежать и OnePyTest.py.

Читайте также:  Javascript для моего телефона

Источник

Интеграция 1С с DLL с помощью Python

Привет Хабр! Недавно я разработал алгоритм для логистики, и нужно было его куда-то пристроить. Помимо веб-сервиса решено было внедрить данный модуль в 1С, и тут появилось довольно много подводных камней.

Начнем с того, что сам алгоритм представлен в виде dll библиотеки, у которой одна точка входа, принимающая JSON строку как параметр, и отдающая 2 колбэка. Первый для отображения статуса выполнения, другой для получения результата. С web-сервисом все довольно просто, у питона есть замечательный пакет ctypes, достаточно подгрузить нужную библиотеку и указать точку входа.

Выглядит это примерно так:

import ctypes def callback_recv(*args): print(args) lib = ctypes.cdll.LoadLibrary('test.dll') Callback = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) my_func = getattr(lib, '_ZN7GtTools4testEPKcPFviS1_E') cb_func = Callback(callback_recv) my_func(ctypes.c_char_p('some data'), cb_func) 

Как можно заметить, точка входа не совсем читабельная. Чтобы найти данную строчку в скомпилировнанных данных, нужно открыть соответствующий файл с расширением .lib и применить утилиту objdump с параметром -D, в выводе легко можно найти нужный метод по названию.

Данное коверканье метода происходит из-за того, что компилятор манглит («mangle» — калечить) название всех точек входа, причем разные компиляторы «калечат» по разному. В примере указан метод полученный MinGW

В 1С все оказалось гораздо менее тривиально. Для подключения dll нужно, чтобы у нее был специальный интерфейс Native API, позволяющий зарегестрировать Внешнюю Компоненту. Все написал по примеру, но ничего не взлетало. Я подумал, что это из-за gcc. Все мои попытки поставить Visual Studio были провальны, то ничего не устанавливалось, то не хватало стандартных библиотек.

Уже засыпая мне в голову пришла гениальная гипотеза. Наверное данную проблему не могли не оставить питонисты, ведь на Питон разработно все, что вообще возможно. А-ля правило интернета 34, только по отношению к чудесному Python. И ведь я оказался прав!

Для python существует пакет win32com который позволяет регестрировать Python объекты, как COM объекты. Для меня это было какой то магией, ведь я даже не очень понимаю что такое COM объект, но знаю что он умеет в 1С.

Пакет pypiwin32 не нужно ставить с помощью pip, а скачать его установщик, т.к. почему-то объекты не регестрировались после установки pip’ом.

Разобравшись с небольшим примером, я взялся за разработку. Для начала нужно создать Объект с интерфейсом идентифицирующим COM-Объект в системе

class GtAlgoWrapper(): # com spec _public_methods_ = ['solve','resultCallback', 'progressCallback',] # методы объекта _public_attrs_ = ['version',] # атрибуты объекта _readonly_attr_ = [] _reg_clsid_ = '' # uuid объекта _reg_progid_= 'GtAlgoWrapper' # id объекта _reg_desc_ = 'COM Wrapper For GTAlgo' # описание объекта def __init__(self): self.version = '0.0.1' self.progressOuterCb = None # . def solve(self, data): # . return '' def resultCallback(self, obj): # . return obj def progressCallback(self, obj): # в колбэк необходимо передавать 1С объект, в котором идет подключение # например ЭтотОбъект или ЭтаФорма if str(type(obj)) = : com_obj = win32com.client.Dispatch(obj) try: # сохраним функцию из 1С (progressCallback) в отдельную переменную self.progressOuterCb = com_obj.progressCallback1C; except AttributeError: raise Exception('"progressCallback" не найден в переданном объекте') return obj 

и конечно опишем его регистрацию

def main(): import win32com.server.register win32com.server.register.UseCommandLine(GtAlgoWrapper) print('registred') if __name__ == '__main__': main() 

Теперь при запуске данного скрипта в системе появится объект GtAlgoWrapper. Его вызов из 1С будет выглядеть вот так:

Функция progressCallback1C(знач, тип) Экспорт Сообщить("значение = " + знач); Сообщить("тип = " + тип); КонецФункции //. Процедура Кнопка1Нажатие(Элемент) //Создадим объект ГТАлго = Новый COMОбъект("GtAlgoWrapper"); //Установим колбэки ГТАлго.progressCalback(ЭтотОбъект); //. Данные = . ; // JSON строка ГТАлго.solve(Данные); КонецПроцедуры 

Таким образом, все попадающие в колбэки даные можно будет обработать. Единственное, что может еще остаться непонятным — как передать данные из dll в 1C:

_dependencies = ['libwinpthread-1.dll', 'libgcc_s_dw2-1.dll', # . 'GtRouting0-0-1.dll'] def solve(self, data): prefix_path = 'C:/release' # должны быть подключены все зависимые библиотеки try: for dep in self._dependencies: ctypes.cdll.LoadLibrary(os.path.join(prefix_path, dep)) # запоминаем библиотеку с нужной нам точкой входа lib = ctypes.cdll.LoadLibrary(os.path.join(prefix_path, 'GtAlgo0-0-1.dll')) except WindowsError: raise Exception('cant load' + dep) solve_func = getattr(lib, '_ZN6GtAlgo5solveEPKcPFviS1_ES3_') # создаем колбэки StatusCallback = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) ResultCallback = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p) scb_func = StatusCallback(self.progressOuterCb) rcb_func = ResultCallback(self.resultOuterCb) # колбэки 1C превратились в функции которые мы передадим в DLL. Magic! if self.resultOuterCb is None: raise Exception('resultCallback function is not Set') if self.progressOuterCb is None: raise Exception('progressCallback function is not Set') # запустим алгоритм solve_func(ctypes.c_char_p(data), scb_func, rcb_func) 

Для успешной работы, в первую очередь требуется вызов python-скрипта, чтобы зарегистрировать класс GtAlgoWrapper, а затем уже можно смело запускать конфигурацию 1С.

Вот так просто можно связать dll библиотеку и 1C с помощью питона, не уползая в сильные дебри.
Всем Магии!

Источник

Native API компонента для использования установленного в системе интерпретатора Python

Компонента позволяет подключать к платформе 1С python-модули, оформленные в стиле внешней компоненты. Кроме этого, она позволяет запускать небольшие фрагменты python-кода прямо из модулей 1С. На данный момент поддерживаются платформы 8.2 — 8.3 x86 Windows.

В системе должен присутствовать совместимый интерпретатор Python. В данном случае это Python 3.3, инсталлятор которого можно скачать на официальном сайте разработчиков: http://www.python.org/ftp/python/3.3.5/python-3.3.5.msi Интерфейс взаимодействия позволяет оформлять модули расширения максимально просто и наглядно. Для примера, вот функциональный аналог образца Native ВК от ИТС:

# -*- coding: utf-8 -*- from interfacing import * from time import sleep, monotonic from threading import Thread class Timer(Thread): def __init__(self, ent): self.done = False self.ent = ent Thread.__init__(self) def run(self): while not self.done: self.ent.ExternalEvent('ComponentNative', 'Timer', str(monotonic())) sleep(5) self.done = False @Component class CAddInNative: def __init__(self): self.m_boolEnabled = False self.AsyncEvent = Timer(self.Enterprise) @Readable('IsEnabled', 'Включен') @property def ePropIsEnabled(self): return self.m_boolEnabled @Writable @ePropIsEnabled.setter def ePropIsEnabled(self, value): self.m_boolEnabled = value @Readable('IsTimerPresent', 'ЕстьТаймер') @property def ePropIsTimerPresent(self): return True @Procedure('Enable', 'Включить') def eMethEnable(self): self.m_boolEnabled = True @Procedure('Disable', 'Выключить') def eMethDisable(self): self.m_boolEnabled = False @Procedure('ShowInStatusLine', 'ПоказатьВСтрокеСтатуса') def eMethShowInStatusLine(self, var): self.Enterprise.SetStatusLine(var) sleep(5) @Procedure('StartTimer', 'СтартТаймер') def eMethStartTimer(self): self.AsyncEvent.start() @Procedure('StopTimer', 'СтопТаймер') def eMethStopTimer(self): self.AsyncEvent.done = True @Function('LoadPicture', 'ЗагрузитьКартинку') def eMethLoadPicture(self, name): with open(name, 'rb') as f: return f.read() @Procedure('ShowMessageBox', 'ПоказатьСообщение') def eMethShowMsgBox(self): info = self.Enterprise.GetInterface(EC_IPlatformInfo) imsgbox = self.Enterprise.GetInterface(EC_IMsgBox) if info is None or imsgbox is None: return if imsgbox.Confirm(info.GetPlatformInfo()[0]): imsgbox.Alert('OK') else: imsgbox.Alert('Cancel') def ExplicitDone(self): self.AsyncEvent.done = True self.AsyncEvent.join()

Тестовая конфигурация

В архиве есть достаточно подробная документация и комплект для тестирования. Чтобы запустить тест, нужно указать путь к компоненте, по которому должен лежать и OnePyTest.py.

Источник

Оцените статью