Проект внешней компоненты для 1С:8 (сделай сам)

15.05.19

Разработка - Разработка внешних компонент

Как их делают? Как регистрируют? С чего начать? Где взять стартовый проект? Вопросов вообще много можно задать, но лучше один раз увидеть!

Скачать файлы

Наименование Файл Версия Размер
Конструктор
.7z 9,20Kb
5
.7z 2 9,20Kb 5 Скачать

Запускаем vs2013 (у меня community).

 

Обычно после этого нужно создавать проект или открывать существующий. Т.к. мы начинаем с нуля, то будем создавать (Файл\Создать\Проект).

В открывшееся окно обычно не вызывает застревания, но мы проговорим и это. Выбираем в дереве "Visual C++", а в списке "Консольное приложение Win32". Теперь трудный момент - нужно придумать название или имя. Проблема обычно в том, что мы не знаем, чего хотим.

Давайте назовем проект SuperI. Нажимаем "Ок" и открывается мастер. К счастью, на первой закладке ничего не нужно вводить и отмечать. Жмем "Далее".

Со второй закладкой все совсем иначе (уже что-то заполнено, а мы еще мало что понимаем). Переставляем отметку с "Консольное приложение" на "Библиотека DLL". Не спешите нажимать "Готово", мы ведь хотим совсем с нуля, правда? Надо отметить пустой проект, а уже потом нажимать "Готово".

Ну вот, это оно - наш пустой проект. В этом месте обычно вспоминают о дисках ИТС и прочем. Нам понадобится кое-что добавить. Прикрепил это к публикации. Лучше сложить файлы в каталог <путь до>\Documents\Visual Studio 2013\ProjectsCPP\SuperI\SuperI, а потом добавить их в проект.

Добавить нужно ряд заголовочных файлов (Проект\Добавить существующий элемент):

1. adapter.h - Базовый интерфейс объекта платформы (для взаимодействия с платформой 1С)

2. memory_adapter.h - Предопределенный класс, для выделения и освобождения памяти в платформе

3. base.h - интерфейс для инициализации компоненты, предопределенный интерфейс описания методов предназначенный для использования платформой 1С,  интерфейс для изменения локализации в компоненте

4. com.h

5. types.h

Добавим еще и файл исходного кода (Проект\Добавить существующий элемент):

1. dllmain.cpp - стандартная точка входа в библиотеку

Увы и ах, но это еще не конец. Нам нужен манифест. Делаем файл с именем MANIFEST.XML в папке с проектом <путь до>\Documents\Visual Studio 2013\ProjectsCPP\SuperI (в этом каталоге лежит папка SuperI с нашим исходным кодом).

А вот и содержание этого файла:

<?xml version="1.0" encoding="UTF-8"?>
<bundle xmlns="http://v8.1c.ru/8.2/addin/bundle">
  <component os="Windows" path="SuperI.dll"     type="native" arch="i386"  />
  <component os="Windows" path="SuperI64.dll"   type="native" arch="x86_64"/>
  <component os="Linux"   path="SuperI.so"      type="native" arch="i386"/>
  <component os="Linux"   path="libSuperI64.so" type="native" arch="x86_64"/>
</bundle>

Важно не перепутать содержание атрибута path! Заметили имя своего проекта в имени конечной библиотеки?

Теперь немного муторное дело - нужно подготовить последний магический файлик с именем SuperI.def в папке исходного кода <путь до>\Documents\Visual Studio 2013\ProjectsCPP\SuperI\SuperI

Содержимое файла (обратите внимание на имя SuperI):

LIBRARY    "SuperI"

EXPORTS
GetClassObject
DestroyObject
GetClassNames

В студии есть "Обозреватель решений". Обычно он даже открыт, но если Вы закрываете всё лишнее, то можно открыть его снова Вид\Обозреватель решений. В окне обозревателя решений в самом верху дерева указан проект, а ниже наше решение "SuperI". Пот по решению и щелкаем правой кнопкой (кнопка на мышке). В самом низу контекстного меню есть пункт "Свойства". Выбираем его.

В открывшемся окне слева опять дерево. Пропускаем "Общие свойства" и переходим "Свойства конфигурации\Компоновщик\Все параметры". В списке (в самом низу почти) есть пункт "Файл определения модуля". Он-то нам и нужен! Нужно изменить его значение на текст: SuperI.def

НАЖИМАЕМ КНОПКУ "OK".

Все, магия кончилась. 

На этом месте у Вас уже есть вполне рабочий инструмент - базовый компонент, который можно наследовать со своей реализацией. Пример такой реализации мы в начале добавим, а позже разберем.

Добавляем файл class.hpp в папку с исходными тестами <путь до>\Documents\Visual Studio 2013\ProjectsCPP\SuperI\SuperI

Теперь можно собрать проект  "Сборка\Собрать решение"

 

 У меня получился такой результат:

 

Это не значит, что у Вас то же самое. Но если получилось успешно собрать, то поздравляю!

Дальше идем в 1С и пишем там такой код:

&НаКлиенте   
перем ДемоКомп;

&НаКлиенте
Процедура ВнешнееСобытие(Источник, Событие, Данные)
	Сообщить(Источник + " " + Событие + " " + Данные);
КонецПроцедуры
&НаКлиенте
Процедура ПриОткрытии(Отказ)
	res=ПодключитьВнешнююКомпоненту("C:\Users\etyurin\Documents\Visual Studio 2013\ProjectsCPP\SuperI\Debug\SuperI.dll", "VK", ТипВнешнейКомпоненты.Native);
	ДемоКомп = Новый("AddIn.VK.myClass");
	ДемоКомп.Свойство_int=-59;
	ДемоКомп.Свойство_double=-10.6598;
	ДемоКомп.Свойство_pchar="увфцв sdfd 4545";
	врем=ДемоКомп.Свойство_pchar;
	ДемоКомп.Свойство_bool=Ложь;
	ДемоКомп.Свойство_tm=ТекущаяДата();
	ДемоКомп.Процедура1(-29);
	res=ДемоКомп.Функция2(-1,-9000);
	res2=ДемоКомп.Функция1();
КонецПроцедуры

Проверяйте работу в отладчике 1С.

Грабли:

1) Нет манифеста

2) Нет магического файла

3) Проект не библиотеки (просто поменяете в свойствах решения "свойства конфигурации\общие" - в списке "тип конфигурации" установите "Динамическая библиотека (.dll)", не забываем нажимать "Ok")

 

Теперь пришло время немного внимательней разобраться. Лучше сразу освоить отладку компоненты в студии. Меню "Отладка\Присоединиться к процессу"

Откроется список процессов, вызовы из которых можно перехватить:

 

 После этого на Ваших точках останова программа будет тормозиться. И можно выяснить в чем же проблема то.

Когда Вы пишете в 1С:

res=ПодключитьВнешнююКомпоненту("C:\Users\etyurin\Documents\Visual Studio 2013\ProjectsCPP\SuperI\Debug\SuperI.dll", "VK", ТипВнешнейКомпоненты.Native);

то первым делом будет запущена функция:

 

BOOL APIENTRY DllMain(HMODULE hModule,
					  DWORD  ul_reason_for_call,
					  LPVOID lpReserved
					  )

Что напряглись? Расслабьтесь в это вникать не нужно.

Как 1С узнает, что в компоненте присутствует? - Ответ 1С получит вызвав функцию GetClassNames():

/*Список доступных типов*/
static const wchar_t Class_Names[] = L"myClass"; //|OtherClass1|OtherClass2

/*Получение списка возможных типов*/
const WCHAR_T* GetClassNames() {
	static WCHAR_T* names;
	wchar_to_WCHAR(names, Class_Names);

	return names;
}
.

Class_Names - это просто массив символов, где через вертикальную черту перечислены все возможные имена классов или их псевдонимы. Функция GetClassNames просто возвращает в 1С эту строку.

1С будет создавать экземпляры каждого типа сразу при подключении, а потом будет использовать их. Если вызвать первый раз Новый("AddIn.VK.myClass"), то будет использован уже созданный при подключении экземпляр, а если вызвать снова Новый("AddIn.VK.myClass") - то будет создан уже новый экземпляр класса.

Получив список возможных имен или псевдонимов классов, платформа, не задумываясь более, создает экземпляр каждого из них вызывая функцию GetClassObject:

long GetClassObject(const WCHAR_T* ex_name, Base** pInterface) {
	if(!*pInterface) {
		wchar_t * name = nullptr;
		WCHAR_to_wchar(name, ex_name);
		if(!wcscmp(name, L"myClass"))
			*pInterface = new myClass();
		delete[] name;
		return (long)*pInterface;
	}
	return 0;
}

Функция GetClassObject устроена совсем примитивно - в ней указано соответствие между именем и классом. Она создает экземпляр и возвращает ссылку на него платформе.

Недостатком типового "решения" 1С является его ориентация не на быструю разработку, а на быстрое понимание принципов работы платформы с компонентой. Поэтому здесь Вы не найдете этого типового примера. 

Когда создается экземпляр класса, то вызывается его конструктор:

/*конструктор*/
	myClass() {
		/*объявляем имя класса доступное из 1С*/
		Base::fill_name(L"myClass");

		/*объявляем свойства доступные из 1С*/
		Base::Prop Props[] = {
			{L"Свойство_int"   , L"Prorp0", true, true},
		    {L"Свойство_double", L"Prorp1", true, true},
			{L"Свойство_pchar" , L"Prorp2", true, true},
			{L"Свойство_bool"  , L"Prorp3", true, true},
			{L"Свойство_tm"    , L"Prorp4", true, true}
		};
		Base::fill_props(Props, sizeof(Props) / sizeof(Base::Prop));

		/*объявляем методы доступные из 1С*/
		Base::Method Methods[] = {
			{L"Функция1", L"Func1", 0},
			{L"Функция2", L"Func2en", 2},
			{L"Процедура1", L"Proc1en", 1, true}
		};		
		Base::fill_methods(Methods, sizeof(Methods) / sizeof(Base::Method));
		
		Prop2 = new char[100];
		std::strcpy(Prop2, "abc абС 123");

		time_t rawtime;
		time(&rawtime);
		Prop4 = *localtime(&rawtime);		
	}

Я искал подходящую реализацию. Теперь я могу о ней рассказать. Первым делом, мы свяжем экземпляр нашего класса с тем псевдонимом, о котором известно 1С:

/*объявляем имя класса доступное из 1С*/
		Base::fill_name(L"myClass");

Платформа обязательно спросит его снова, и нам нужно не забыть вернуть то же наименование. Чтобы разобраться в назначение последующего кода нужно держать в голове схему опроса платформой возможностей экземпляра класса. Платформу будет интересовать количество доступных свойств (GetNProps). Зная количество 1С будет запрашивать свойства по их индексам (начиная с 0).  Имя свойства (GetPropName), можно ли прочитать значение свойства (IsPropReadable), можно ли изменить (IsPropWritable), получить значение свойства (GetPropVal) или установить значение (SetPropVal). Во всех этих случаях платформа будет передавать индекс. А вот тип значения 1С не беспокоит - вы получите коробку, в которой может быть сразу любой тип (Ваша компонента будет решать, что с этой коробкой делать).

/*объявляем свойства доступные из 1С*/
		Base::Prop Props[] = {
			{L"Свойство_int"   , L"Prorp0", true, true},
		        {L"Свойство_double", L"Prorp1", true, true},
			{L"Свойство_pchar" , L"Prorp2", true, true},
			{L"Свойство_bool"  , L"Prorp3", true, true},
			{L"Свойство_tm"    , L"Prorp4", true, true}
		};
		Base::fill_props(Props, sizeof(Props) / sizeof(Base::Prop));

В констукторе экземпляра заполняется массив структур Props. С каждым свойством связано два имени (русское и английское) и два свойства возможность получить значение и возможность изменить значение. Так они и перечислены:

{L"Русское_Имя", L"Английское_Имя", <возможность получить значение> , < возможность изменить значение>}

Аналогично обстоят дела и с функциями/процедурами. Также все методы вызываются по номерам:

/*объявляем методы доступные из 1С*/
		Base::Method Methods[] = {
			{L"Функция1", L"Func1", 0},
			{L"Функция2", L"Func2en", 2},
			{L"Процедура1", L"Proc1en", 1, true}
		};		
		Base::fill_methods(Methods, sizeof(Methods) / sizeof(Base::Method));

Требуется указать русское имя, английское, количество параметров и признак процедуры:

{L"Русское_Имя", L"Английское_Имя", <количество параметров> , <признак процедуры>}

Типы параметров платформу не интересуют. Она просто будет следить за нужным их количеством. Все параметры будут переданы массивом коробок, с указанием количества элементов в массиве. Если признак процедуры равен true, то возвращаемое значение в 1С передать не получится. 

Вот и весь конструктор, который требуется оформить.

Посмотрим на детали обращения к свойствам (с методами повторится тот же алгоритм). Когда в коде 1С мы пишем:

врем=ДемоКомп.Свойство_pchar;

то платформе понадобится номер свойства (ведь в коде только имя). Немедленно последует вызов функции FindProp:

long Base::FindProp(const WCHAR_T* ex_name) {
	long res = -1;
	wchar_t * temp = nullptr;
	WCHAR_to_wchar(temp, ex_name);
		
	for(long i = 0; res == -1 && i < cnt_props; ++i)
		if(!wcscmp(Props[i]->en, temp) || !wcscmp(Props[i]->ru, temp))
			res = i;
	
	delete[] temp;

	return res;
}

Эта функция просто вернет индекс свойства из массива Props (помните в конструкторе заполняли?) Получив индекс платформа будет выяснять возможность прочитать свойство IsPropReadable:

 

bool Base::IsPropReadable(const long num) {
	if(num < cnt_props)
		return Props[num]->r;
	else
		return false;
}

Функция просто вернет соответствующее поле структуры свойств Props. Если свойство можно прочитать, то от платформы последует вызов  GetPropVal:

 

/*Получение свойства*/
	virtual bool ADDIN_API GetPropVal(const long num, tVariant* var) override {
		switch(num) {
			case 0: //Свойство_int
				TV_VT(var) = VTYPE_I4; //выставляем тип
				TV_I4(var) = Prop0;    //выставляем значение
				break;
			case 1: //Свойство_double
				TV_VT(var) = VTYPE_R8;  //выставляем тип
				TV_R8(var) = Prop1;     //выставляем значение
				break;
			case 2: //Свойство_pchar
				TV_VT(var) = VTYPE_PSTR;  //выставляем тип
				var->pstrVal = Prop2;     //сразу указатель на строку
				var->strLen  = std::strlen(Prop2);
				break;
			case 3: //Свойство_bool
				TV_VT(var) = VTYPE_BOOL; //выставляем тип
				TV_BOOL(var) = Prop3;    //выставляем значение
				break;
			case 4: //Свойство_tm
				TV_VT(var) = VTYPE_TM; //выставляем тип
				var->tmVal = Prop4;    //выставляем значение
				break;
			default:
				return false;
		}
		return true;
	}

Функция заполняет коробку tVariant* согласно индекса переданного свойства. Сама коробка в деталях описана в types.h

Если в коде 1С написать:

ДемоКомп.Свойство_int=-59;

То последуют вызовы FindProp и IsPropWritable (можно ли изменять свойство):

 

bool Base::IsPropWritable(const long num) {
	if(num < cnt_props)
		return Props[num]->w;
	else
		return false;
}

Наша функция просто вернет соответствующее поле структуры Props, по указанному платформой индексу. Если свойство можно менять, то последует вызов SetPropVal с указанием индекса свойства:

 

/*Установка свойства*/
	virtual bool ADDIN_API SetPropVal(const long num, tVariant * var) override {
		switch(num) {
			case 0:
				if(TV_VT(var) != VTYPE_I4)
					return false;
				Prop0 = TV_I4(var);
				break;
			case 1:
				if(TV_VT(var) != VTYPE_R8)
					return false;
				Prop1 = TV_R8(var);
				break;
			case 2:
				if(TV_VT(var) == VTYPE_PSTR) { 
					delete[] Prop2;
					size_t len = std::strlen(var->pstrVal);
					Prop2 = new char[len + 1];
					std::strncpy(Prop2, var->pstrVal, len + 1);
					break;
				} else if(TV_VT(var) == VTYPE_PWSTR) {
					delete[] Prop2;
					WCHAR_to_char(Prop2, var->pwstrVal);
					break;
				} else
					return false;
			case 3:
				if(TV_VT(var) != VTYPE_BOOL)
					return false;
				Prop3 = TV_BOOL(var);
				break;
			case 4:
				if(TV_VT(var) != VTYPE_TM)
					return false;
				Prop4 = var->tmVal;
				break;
			default:
				return false;
		}
		return true;
	}

 Платформа "любит" передавать строки через указатель WCHAR_T*

Ну что страшно пока? Ничего, скоро поймете, что самое страшное я за Вас сделал и Вам осталось только приятная часть. Кстати со свойствами больше ничего и нет. Они перечислены как члены данных в классе:

private:
	int     Prop0 = -113; 
	double  Prop1 = 7.65;
	char  * Prop2 = nullptr;
	bool    Prop3 = true;
	tm      Prop4;

 Полегчало?

Вы можете просто написать такой класс:

class myClass: public Base {
public:
	/*конструктор*/
	myClass() {
		/*объявляем имя класса доступное из 1С*/
		Base::fill_name(L"myClass");
        }
};

и экземпляр его уже можно создать  строчкой:

ДемоКомп = Новый("AddIn.VK.myClass");

только он совсем пустой и ничего не делает.

Пришла пора взглянуть на методы. Это процедуры и функции. Сведения о них платформа получает последовательно. В начале - общее количество всех методов GetNMethods, потом количество формальных параметров конкретного метода GetNParams (просто по индексу, как обычно), дальше платформа захочет выяснить нужно ли ей принимать возвращаемое значение HasRetVal. И в конце последует вызов метода CallAsProc (для процедуры) или CallAsFunc(для функции). Вот смотрите пример:

/*Методы*/
	virtual       bool     ADDIN_API CallAsProc(const long num, tVariant* paParams, const long len) override {
		bool res = false;
		if(num < cnt_methods && Methods[num]->is_proc) {
			switch(num) {
				case 2:
					res = Proc1(paParams, len);
					break;
				default:
					res = false;
			}
		}
		return res; 
	} 
	virtual       bool     ADDIN_API CallAsFunc(const long num, tVariant* pvarRetValue, tVariant* paParams, const long len) override {
		bool res = false;
		if(num < cnt_methods && !Methods[num]->is_proc) {
			switch(num) {
				case 0:
					res = Func1(pvarRetValue, paParams, len);
					break;
				case 1:
					res = Func2(pvarRetValue, paParams, len);
					break;		
				default:
					res = false;
			}
		}
		return res;		
	} 

	bool Proc1(tVariant* paParams, const long lSizeArray) {
		bool res = true;
		if(paParams[0].vt == VTYPE_I4) //Проверка, что пришло целое число
			this->Prop0 = paParams[0].lVal; //значение
		else
			res = false;
		return res; 
	}
	bool Func2(tVariant* pvarRetValue, tVariant* paParams, const long len) {
		bool res = true;
		int a,b;
		if(paParams[0].vt == VTYPE_I4) //Проверка, что пришло целое число
			a = paParams[0].lVal; //значение
		else
			res = false;
		if(paParams[1].vt == VTYPE_I4) //Проверка, что пришло целое число
			b=paParams[1].lVal; //значение
		else
			res = false;
		
		if(res) {
			pvarRetValue->vt = VTYPE_I4; //указываем возвращаемый тип
			pvarRetValue->lVal = a + b;  //указываев возвращаемое значение (выполнять pMemoryAdapter->AllocMemory((void**)&pvarRetValue->lVal, size_in_byte) здесь не требуется, т.к. передаем значение) 
		}

		return res; 
	}
	bool Func1(tVariant* pvarRetValue, tVariant* paParams, const long len) {
		bool res = true;
		size_t l = std::strlen(Prop2) + 1;
		pvarRetValue->vt = VTYPE_PSTR;
		pvarRetValue->strLen = l;
		/*нужно аллоцировать место в структуре возвращаемой в 1С*/
		pMemoryAdapter->AllocMemory((void**)&pvarRetValue->pstrVal, l*sizeof(char));
		std::memcpy(pvarRetValue->pstrVal, Prop2, l*sizeof(char));

		return res;
	}

Платформа передает индекс метода, коробку с параметрами и количество элементов в коробке. Вам не нужно беспокоиться о количестве элементов (будет ровно столько, сколько вы указали в конструкторе). А вот с выковыриванием нужных Вам значений из коробок придется постараться. Для этого обязательно выясняйте значение поля vt, чтоб знать как извлечь значение.

Как только в 1С переменная ДемоКомп покинет границы видимости, сразу последует вызов DestroyObject:

/*Уничтожение экземпляра*/
long DestroyObject(Base** pIntf) {
	if(!*pIntf)
		return -1;

	delete *pIntf;
	*pIntf = 0;
	return 0;
}

Платформа так сообщает компоненте, что экземпляр больше не нужен.

Заметили, что компонента с типом Native не требует регистрации? 

Проект внешней компоненты

См. также

Медиадисплей. Рекламный информационный монитор для покупателя.

Разработка внешних компонент POS терминал Рабочее место Розничная торговля Платформа 1С v8.3 1С:Комплексная автоматизация 1.х 1С:Управление торговлей 10 1С:Розница 2 1С:Управление нашей фирмой 1.6 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Розничная и сетевая торговля (FMCG) Рестораны, кафе и фаст-фуд Реклама, PR и маркетинг Управленческий учет Платные (руб)

Монитор покупателя может отображать текущую покупку на кассовом месте, показывать видеорекламу, баннеры, во время простоя разворачивать рекламу на весь экран. Можно использовать в качестве графического меню-борда в кафе и видеовывески. Управление выводом на телевизор через hdmi-приставку на базе Windows или Android. В качестве устройства отображения можно использовать Android-планшеты, фоторамки с Android, монитор любого Windows-компьютера, доступного по сети. Настраивается ЛЮБОЙ ДИЗАЙН экрана!

16800 руб.

30.05.2017    52104    34    69    

43

Внешняя компонента для сканирования (замена TWAIN-компоненты БСП) (Native Win 32/64)

Разработка внешних компонент Платформа 1С v8.3 Конфигурации 1cv8 Платные (руб)

Внешняя компонента позволяет работать c TWAIN-совместимым оборудованием (сканерами, камерами) . Полностью совместима со стандартной TWAIN-компонентой из БСП и может применяться как ее замена без изменения вызовов, при этом может работать с 64-разрядной платформой, а так же имеет расширенную функциональность, например, сохранение результата непосредственно в PDF без использования сторонних утилит. Прекрасно работает на сервере, тонком клиенте и веб-клиенте (проверена работа в браузерах Google Chrome, Mozilla Firefox и Microsoft Internet Explorer).

2400 руб.

12.05.2020    26244    131    99    

83

Внешняя компонента для подключения 1С к телефонии Asterisk

Разработка внешних компонент Телефония, SIP Платформа 1С v8.3 Конфигурации 1cv8 Россия Платные (руб)

Внешняя компонента выполнена по технологии Native API для 1С 8.х, обеспечивает доступ к программным АТС Asterisk (FreePBX, Elastix) через AMI интерфейс. Через него можно управлять многими функциями Asterisk (определение номеров, перевод звонков, набор телефона и т. д.)

2400 руб.

04.05.2018    44976    117    64    

60

Внешняя компонента печати PDF (Native Win 32/64)

Разработка внешних компонент Платформа 1С v8.3 Конфигурации 1cv8 Платные (руб)

Внешняя компонента позволяет печатать PDF файлы непосредственно из 1С, не используя при этом сторонних программ. Прекрасно работает на сервере, тонком клиенте и веб-клиенте. Основана на проекте PDFium из состава проекта Chromium/Chrome

1500 руб.

17.09.2018    35081    104    123    

111

Мастер создания внешних компонент 1С (технология COM) для DELPHI 6/7/8/2005/2006/2007/2008/2010/XE/XE2/XE3

Разработка внешних компонент Платформа 1С v8.3 Платные (руб)

Средство для сверхбыстрой разработки внешних компонент 1С:Предприятия 7.7 и 8 по технологии COM на всех версиях DELPHI, начиная с 6.

2000 руб.

28.03.2013    53999    35    14    

68

QR-код с логотипом компании (обычная и управляемая форма)

Разработка внешних компонент Платформа 1С v8.3 Управляемые формы Конфигурации 1cv8 Платные (руб)

Как известно, стремление сделать свою рекламную продукцию запоминающейся и выделяющейся — верный путь к успеху. Сегодня, мы поговорим с вами о том, что можно сделать с обычным черно-белым QR-кодом, чтобы он стал более живым и привлекательным. Если вам не терпится попробовать сделать QR-код с логотипом компании, то эта обработка для вас!

2400 руб.

22.06.2016    30840    4    4    

8

Внешняя компонента 1С и С++. Продолжаем разговор.

Разработка внешних компонент Платформа 1С v8.3 Бесплатно (free)

А давайте запилим 8.3.26 до релиза, или оповещение с сервера...

19.02.2024    4039    starik-2005    28    

52

Внешние компоненты 1С и язык C++

Разработка внешних компонент Механизмы платформы 1С Платформа 1С v8.3 Бесплатно (free)

Некоторые практические аспекты создания внешних компонент на языке С++ для платформы 1С 8.3++.

26.01.2024    4778    starik-2005    32    

39
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. DoctorRoza 14.03.16 13:45 Сейчас в теме
Для полной ясности как такое работает, нужен хороший пример! Например, реализуйте компоненту, которая делает задержку выполнения кода или, скажем, цикла. Мне как-то такое потребовалось, не помню зачем.
2. ture 606 14.03.16 14:01 Сейчас в теме
(1) DoctorRoza, Не вопрос!
Сейчас работаю над простой реализацией. У Вас будет простой вариант наследования базового компонента, а вся рутина будет реализована в базовом классе. Опишу порядок взаимодействия с 1С в той мере, в которой это будет полезно конечному разработчику.
DoctorRoza; +1 Ответить
3. Evil Beaver 8107 14.03.16 15:22 Сейчас в теме
А какие преимущества указанного способа по сравнению с шаблоном проекта C++ и методикой "Разработки внешних компонент" с диска ИТС?

Например, я (как пользователь инфостарта) могу взять шаблон проекта и методику вот отсюда http://infostart.ru/public/184119/. Чем ваш вариант отличается от предлагаемого на ИТС?

Мне просто не хочется тратить 10sm, чтобы всего лишь выяснить разницу.
avz_1C; vdgb_bushkov; +2 1 Ответить
4. ture 606 14.03.16 17:12 Сейчас в теме
(3) Evil Beaver, В чем преимущество моего варианта перед Вашим? Ну... во-первых, я потерял дар речи...
Я опирался на Ваше описание!!! Нету преимущества здесь.

Ладно, по сути дела. Для меня оказалось неожиданностью такое внимание к "создать проект библиотеки DLL в студии" (это ведь черновой вариант по моим понятиям). Даже появилась задумка "создать проект консольного приложения (в картинках ;)"

На диске ИТС лежит готовый проект. И что мало кто знает, как создать его с 0? В общем, это может объяснить популярность.

Я столкнулся с тем, что проект на ИТС написан ровно для того, чтобы быстро разобраться как и что. Но он не может служить основой реальной разработки. Я решил исправить недочет и стал выкладывать решение последовательно. В какой-то момент свое решение я стал переписывать в третий раз и уже с учетом большинства своих хочу. Но версию с фрагментами ИТС решил выложить, чтоб не пропадало. Сейчас я готовлю рабочий проект с базовым классом, который удобно и просто наследовать (собственно пишу с самого начала для себя, чтоб удобней было быстро стартовать).
10. Evil Beaver 8107 15.03.16 13:33 Сейчас в теме
(4) ture, нет никакого "моего" варианта. Я просто документировал тот шаблон, который лежит на ИТС.
А вопрос мой был про то - что из статьи непонятно, что именно лежит в архиве. Отличается ли оно от ИТС-ного шаблона и в какую сторону.
11. ture 606 15.03.16 14:34 Сейчас в теме
(10) Evil Beaver, в архиве просто компоненты использованные в статье:
adapter.h
memory_adapter.h
base.h
com.h
types.h
SuperI.def
MANIFEST.XML

Эти файлы наиболее близки к тому, что на ИТС. Шаблон 1С весьма своеобразен. Особенно порадовал базовый класс корпорации.
Вы документировали механизм взаимодействия 1с со своими компонентами ;) Я уже третий день рою эту тему... уже владею основами. Сейчас усердно протираю глаза и пытаюсь понять "как это нельзя массив передать в native dll?! шел год 2016"

Я не добавлял в архив человеческую реализацию базового класса, который можно легко наследовать. Я только работаю над ним, но что-то выложил в комментариях уже.
21. Synoecium 778 16.03.16 06:27 Сейчас в теме
(3) Evil Beaver, Ваша статья идеально подходит для новичков, которым надо разобраться как создавать внешние компоненты. Здесь же написан какой-то сумбур. Вроде я понимаю о чем речь, но как то смутно, при этом я сам ковырялся с внешними компонентами. В общем автору пока минус, в надежде на то, что он выложит волшебный класс, про который много говорит. За что 10 sm вообще не понятно.
22. ture 606 16.03.16 08:51 Сейчас в теме
(21) Synoecium, всегда приятно читать такие комменты, они расслабляют и позволяют не напрягаться. Компонент я выложил в своих комментах.
5. ture 606 15.03.16 10:47 Сейчас в теме
http://bb3x.ru/blog/vneshnie-komponentyi-v-1s-8-2/
Вы намереваетесь использовать пример от 1С. Я дополнил его комментариями.
Немного поясню детали стандартной реализации:
1) 1С вызовет GetClassNames(), что бы узнать список возможных классов (в примере 1С он один)
2) 1С создаст экземпляр каждого из возможных классов (в GetClassObject будет передавать имена классов последовательно, сразу все и создает)
3) 1С инициализирует каждый класс Init, setMemManager, SetLocale (последний на случай, если компонента умеет на разных языках отвечать)
4) 1С спросит версию у каждого класса GetInfo и выяснит его имя RegisterExtensionAs (для строки Новый("AddIn.VK.ture"))
Дальше все стихнет, пока не появится следующий код (вызов методов и свойств)
Свойства:
1) В начале 1С будет выяснять сколько всего свойств в конкретном классе GetNProps
2) Потом будет получать имя каждого свойства по номеру GetPropName (последний параметр - рус или en)
3) Если 1С встречает в коде свойство, то вызывает FindProp (чтоб узнать номер свойства), потом IsPropReadable (чтоб узнать, можно ли его читать) или IsPropWritable (чтоб выяснить, можно ли писать в него). В финале вызывается GetPropVal (для чтения свойства, зная его порядковый номер) или SetPropVal (для записи)
С функциями то же самое. Это рефлексия типов такая у 1С.

Значения передаются через структуру _tVariant. У нее есть тип vt и куча значений в union (заполнено осмыслено какое-то одно).
/*Получение свойства*/
	virtual bool ADDIN_API GetPropVal(const long num, tVariant* var) override {
		switch(num) {
			case 0: //Свойство_int
				TV_VT(var) = VTYPE_I4; //выставляем тип
				TV_I4(var) = Prop0;    //выставляем значение
				break;
			case 1: //Свойство_double
				TV_VT(var) = VTYPE_R8;  //выставляем тип
				TV_R8(var) = Prop1;     //выставляем значение
				break;
			case 2: //Свойство_pchar
				TV_VT(var) = VTYPE_PSTR;  //выставляем тип
				var->pstrVal = Prop2;     //сразу указатель на строку
				var->strLen  = std::strlen(Prop2);
				break;
			case 3: //Свойство_bool
				TV_VT(var) = VTYPE_BOOL; //выставляем тип
				TV_BOOL(var) = Prop3;    //выставляем значение
				break;
			case 4: //Свойство_tm
				TV_VT(var) = VTYPE_TM; //выставляем тип
				var->tmVal = Prop4;    //выставляем значение
				break;
			default:
				return false;
		}
		return true;
	}
	/*Установка свойства*/
	virtual bool ADDIN_API SetPropVal(const long num, tVariant * var) override {
		switch(num) {
			case 0:
				if(TV_VT(var) != VTYPE_I4)
					return false;
				Prop0 = TV_I4(var);
				break;
			case 1:
				if(TV_VT(var) != VTYPE_R8)
					return false;
				Prop1 = TV_R8(var);
				break;
			case 2:
				if(TV_VT(var) == VTYPE_PSTR) { 
					delete[] Prop2;
					size_t len = std::strlen(var->pstrVal);
					Prop2 = new char[len + 1];
					std::strncpy(Prop2, var->pstrVal, len + 1);
					break;
				} else if(TV_VT(var) == VTYPE_PWSTR) {
					delete[] Prop2;
					WCHAR_to_char(Prop2, var->pwstrVal);
					break;
				} else
					return false;
			case 3:
				if(TV_VT(var) != VTYPE_BOOL)
					return false;
				Prop3 = TV_BOOL(var);
				break;
			case 4:
				if(TV_VT(var) != VTYPE_TM)
					return false;
				Prop4 = var->tmVal;
				break;
			default:
				return false;
		}
		return true;
	} 
Показать
6. ture 606 15.03.16 11:05 Сейчас в теме
В примере выше я использовал функции:
/*Конвертация wchar_t* -> WCHAR_T* */
size_t wchar_to_WCHAR(WCHAR_T * &Dest, const wchar_t* Source, size_t len) {
	if(len == 0) {//если размер задан, то и место уже зарезервировано
		len = wchar_len(Source) + 1;
		Dest = new WCHAR_T[len];
	}
	memset(Dest, 0, len*sizeof(WCHAR_T));	
	for(size_t i = 0; i < len && Source[i]; ++i)
		Dest[i] = (WCHAR_T)Source[i];			

	return len;
}
/*Конвертация wchar_t* -> char* */
size_t wchar_to_char(char * &Dest, const wchar_t* Source, size_t len) {
	if(len == 0) {//если размер задан, то и место уже зарезервировано
		len = wchar_len(Source) + 1;
		Dest = new char[len];
	}
	len = wcstombs(Dest, Source, len);
	Dest[len - 1] = '\0';
	return len;
}
/*Конвертация WCHAR_T** -> wchar_t* */
size_t WCHAR_to_wchar(wchar_t * &Dest, const WCHAR_T* Source, size_t len) {
	if(len==0){//если размер задан, то и место уже зарезервировано
		len = WCHAR_len(Source) + 1;
		Dest = new wchar_t[len];
	}
	memset(Dest, 0, len*sizeof(wchar_t));
	for(size_t i = 0; i < len && Source[i]; ++i)
		Dest[i] = (wchar_t)Source[i];

	return len;
}
/*Конвертация WCHAR_T** -> char* */
size_t WCHAR_to_char(char * &Dest, const WCHAR_T* Source, size_t len) {
	wchar_t * temp;
	WCHAR_to_wchar(temp, Source);
	len=wchar_to_char(Dest, temp,len);
	delete[] temp;

	return len;
}
/*Вычисление длинны строки WCHAR_T* */
size_t WCHAR_len(const WCHAR_T* Source) {
	size_t res = 0;
	while(Source[res]) 	++res;

	return res;
}
/*Вычисление длинны строки wchar_t* */
size_t wchar_len(const wchar_t* Source) {
	size_t res = 0;
	while(Source[res]) 	++res;

	return res;
}

Показать
7. ture 606 15.03.16 11:07 Сейчас в теме
1С создает экземпляры сразу:
/*Список доступных типов (регистрируются в RegisterExtensionAs тем же именем)*/
static const wchar_t Class_Names[] = L"myClass"; //|OtherClass1|OtherClass2

/*ЭКСПОРТИРУЕМЫЕ МЕТОДЫ*/
/*Получение экземпляра по имени*/
long GetClassObject(const WCHAR_T* ex_name, Base** pInterface) {
	if(!*pInterface) {
		wchar_t * name = nullptr;
		WCHAR_to_wchar(name, ex_name);
		if(!wcscmp(name, L"myClass"))
			*pInterface = new myClass();
		delete[] name;
		return (long)*pInterface;
	}
	return 0;
}
/*Получение списка возможных типов*/
const WCHAR_T* GetClassNames() {
	static WCHAR_T* names;
	wchar_to_WCHAR(names, Class_Names);

	return names;
}
Показать
8. ture 606 15.03.16 11:09 Сейчас в теме
Лучше использовать не пример от 1С, а свой базовый класс:
/***************************************************
*  Данный класс предназначен для наследования      *
****************************************************/
class Base:public IInitDoneBase, public ILanguageExtenderBase, public LocaleBase {
protected:
	/*вложенные типы (для "рефлексии типа из 1С")*/
	struct Prop {	
		wchar_t *ru, *en;
		bool    r,w;
		void Init(const wchar_t *ru, const wchar_t *en, bool r, bool w) {
			/*копируем имена свойств ru и en*/
			size_t len = wcslen(ru) + 1;
			this->ru = new wchar_t[len];
			memcpy(this->ru, ru, len*sizeof(wchar_t));
			len = wcslen(en) + 1;
			this->en = new wchar_t[len];
			memcpy(this->en, en, len*sizeof(wchar_t));
			/*копируем призначки чтения и записи*/
			this->r = r;
			this->w = w;
		}
		Prop(const wchar_t *ru, const wchar_t *en, bool r, bool w) {
			Init(ru, en, r, w);
		}
		Prop(const Prop & other) {
			Init(other.ru, other.en, other.r, other.w);
		}
		~Prop() {
			delete[] ru, en;
			ru = en = nullptr;
		}
	};
	struct Method {
		wchar_t *ru, *en;
		bool    is_proc;
		void Init(const wchar_t *ru, const wchar_t *en, bool is_proc) {
			/*копируем имена методов ru и en*/
			size_t len = wcslen(ru) + 1;
			this->ru = new wchar_t[len];
			memcpy(this->ru, ru, len*sizeof(wchar_t));
			len = wcslen(en) + 1;
			this->en = new wchar_t[len];
			memcpy(this->en, en, len*sizeof(wchar_t));
			/*копируем признак процедуры*/
			this->is_proc = is_proc;
		}
		Method(const wchar_t *ru, const wchar_t *en, bool is_proc) {
			Init(ru, en, is_proc);
		}
		Method(const Method & other) {
			Init(other.ru, other.en, other.is_proc);
		}
		~Method() {
			delete[] ru, en;
			ru = en = nullptr;
		}
	};
	
	/*адаптеры*/
	Adapter            * pAdapter;
	MemoryAdapter      * pMemoryAdapter;
	
	/*Локальное имя*/
	wchar_t * name;
	/*свойства, методы и классы*/
	long cnt_props;
	Prop ** Props;	
	long cnt_methods;
	Method ** Methods;
	/*заполнение свойств и методов*/
	void fill_name(const wchar_t * name);
	void fill_props(const Prop * Prors = nullptr, long cnt_props = 0);
	void fill_methods(const Method * Methods = nullptr, long cnt_methods = 0);
		
	virtual void addError(uint32_t wcode, const wchar_t* source, const wchar_t* descriptor, long code);

public:
	/*Деструктор*/
	virtual ~Base();
	/*Конструктор*/
	Base():cnt_props(0), Props(nullptr), cnt_methods(0), Methods(nullptr), pAdapter(nullptr), pMemoryAdapter(nullptr), name(nullptr) {}

	/*Инициализация и завершение*/
	virtual bool ADDIN_API Init(void*)                   override;
	virtual bool ADDIN_API setMemManager(void* mem)      override;
	virtual long ADDIN_API GetInfo()                     override;
	virtual void ADDIN_API Done()                        override;
	/*смена локализации*/
	virtual void ADDIN_API SetLocale(const WCHAR_T* loc) override;

	/*Расширение языка*/
	/*Регистрация расширения под конкретным именем*/
	virtual       bool     ADDIN_API RegisterExtensionAs(WCHAR_T**) override;

	virtual       long     ADDIN_API GetNProps() override  { return cnt_props; }
	virtual       long     ADDIN_API FindProp(const WCHAR_T* wsPropName) override;
	virtual const WCHAR_T* ADDIN_API GetPropName(long lPropNum, long lPropAlias) override;
	virtual       bool     ADDIN_API IsPropReadable(const long lPropNum) override;
	virtual       bool     ADDIN_API IsPropWritable(const long lPropNum) override;
	virtual       bool     ADDIN_API GetPropVal(const long num, tVariant* var) override { return false; } //заглушка на случай отсутствия свойств
	virtual       bool     ADDIN_API SetPropVal(const long num, tVariant* val) override { return false; } //заглушка на случай отсутствия свойств
	
	virtual       long     ADDIN_API GetNMethods() override  { return cnt_methods; }
	virtual       long     ADDIN_API FindMethod(const WCHAR_T* wsMethodName) override;
	virtual const WCHAR_T* ADDIN_API GetMethodName(const long lMethodNum, const long lMethodAlias) override;
	virtual       long     ADDIN_API GetNParams(const long lMethodNum) override { return 0; }                                                                        //заглушка на случай отсутствия методов
	virtual       bool     ADDIN_API GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) override  { return false; }          //заглушка на случай отсутствия методов
	virtual       bool     ADDIN_API HasRetVal(const long lMethodNum) override;	
	virtual       bool     ADDIN_API CallAsProc(const long lMethodNum, tVariant* paParams, const long lSizeArray) override { return false; };                        //заглушка на случай отсутствия методов
	virtual       bool     ADDIN_API CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) override { return false; } //заглушка на случай отсутствия методов
};
Показать
9. ture 606 15.03.16 11:11 Сейчас в теме
Свой класс становится проще при повторном использовании:
#include "base.h"
#include <ctime>

#ifndef CLASS_H
#define CLASS_H

class myClass: public Base {
private:
	int     Prop0 = -113; 
	double  Prop1 = 7.65;
	char  * Prop2 = nullptr;
	bool    Prop3 = true;
	tm      Prop4;
public:
	/*конструктор*/
	myClass() {
		/*объявляем имя класса доступное из 1С*/
		Base::fill_name(L"myClass");

		/*объявляем свойства доступные из 1С*/
		Base::Prop Props[] = {
			{L"Свойство_int"   , L"Prorp0", true, true},
		    {L"Свойство_double", L"Prorp1", true, true},
			{L"Свойство_pchar" , L"Prorp2", true, true},
			{L"Свойство_bool"  , L"Prorp3", true, true},
			{L"Свойство_tm"    , L"Prorp4", true, true}
		};
		Base::fill_props(Props, sizeof(Props) / sizeof(Base::Prop));

		/*объявляем методы доступные из 1С*/
		Base::Method Methods[] = {
			{L"Функция1", L"Func1", false},
		    {L"Функция2", L"Func2", false},
		    {L"Процедура1", L"Proc1", true}
		};
		Base::fill_methods(Methods, sizeof(Methods) / sizeof(Base::Method));

		Prop2 = new char[100];
		std::strcpy(Prop2, "abc абС 123");

		time_t rawtime;
		time(&rawtime);
		Prop4 = *localtime(&rawtime);		
	}
	~myClass() { 
		delete[] Prop2; 		
	}
	
	/*Получение свойства*/
	virtual bool ADDIN_API GetPropVal(const long num, tVariant* var) override {
		switch(num) {
			case 0: //Свойство_int
				TV_VT(var) = VTYPE_I4; //выставляем тип
				TV_I4(var) = Prop0;    //выставляем значение
				break;
			case 1: //Свойство_double
				TV_VT(var) = VTYPE_R8;  //выставляем тип
				TV_R8(var) = Prop1;     //выставляем значение
				break;
			case 2: //Свойство_pchar
				TV_VT(var) = VTYPE_PSTR;  //выставляем тип
				var->pstrVal = Prop2;     //сразу указатель на строку
				var->strLen  = std::strlen(Prop2);
				break;
			case 3: //Свойство_bool
				TV_VT(var) = VTYPE_BOOL; //выставляем тип
				TV_BOOL(var) = Prop3;    //выставляем значение
				break;
			case 4: //Свойство_tm
				TV_VT(var) = VTYPE_TM; //выставляем тип
				var->tmVal = Prop4;    //выставляем значение
				break;
			default:
				return false;
		}
		return true;
	}
	/*Установка свойства*/
	virtual bool ADDIN_API SetPropVal(const long num, tVariant * var) override {
		switch(num) {
			case 0:
				if(TV_VT(var) != VTYPE_I4)
					return false;
				Prop0 = TV_I4(var);
				break;
			case 1:
				if(TV_VT(var) != VTYPE_R8)
					return false;
				Prop1 = TV_R8(var);
				break;
			case 2:
				if(TV_VT(var) == VTYPE_PSTR) { 
					delete[] Prop2;
					size_t len = std::strlen(var->pstrVal);
					Prop2 = new char[len + 1];
					std::strncpy(Prop2, var->pstrVal, len + 1);
					break;
				} else if(TV_VT(var) == VTYPE_PWSTR) {
					delete[] Prop2;
					WCHAR_to_char(Prop2, var->pwstrVal);
					break;
				} else
					return false;
			case 3:
				if(TV_VT(var) != VTYPE_BOOL)
					return false;
				Prop3 = TV_BOOL(var);
				break;
			case 4:
				if(TV_VT(var) != VTYPE_TM)
					return false;
				Prop4 = var->tmVal;
				break;
			default:
				return false;
		}
		return true;
	}

};

#endif
Показать

Я еще работаю над приятном видом. Что-то будет меняться.
12. TSSV 1144 15.03.16 18:24 Сейчас в теме
"Я не добавлял в архив человеческую реализацию базового класса, который можно легко наследовать." Как использовать скачанный архив, посоветуйте пожалуйста.
13. ture 606 15.03.16 18:52 Сейчас в теме
(12) TSSV, это dllmain.cpp
// dllmain.cpp : Defines the entry point for the DLL application.
#include "Base.h"
#include "class.hpp"

#ifndef __linux__
#include <windows.h>
#endif //__linux__
#ifndef __linux__
BOOL APIENTRY DllMain(HMODULE hModule,
					  DWORD  ul_reason_for_call,
					  LPVOID lpReserved
					  ) {
	switch(ul_reason_for_call) {
		case DLL_PROCESS_ATTACH:
		case DLL_THREAD_ATTACH:
		case DLL_THREAD_DETACH:
		case DLL_PROCESS_DETACH:
			break;
	}
	return TRUE;
}
#endif //__linux__

/*ПРОЧИЕ МЕТОДЫ*/
/*Конвертация wchar_t* -> WCHAR_T* */
size_t wchar_to_WCHAR(WCHAR_T * &Dest, const wchar_t* Source, size_t len) {
	if(len == 0) {//если размер задан, то и место уже зарезервировано
		len = wchar_len(Source) + 1;
		Dest = new WCHAR_T[len];
	}
	memset(Dest, 0, len*sizeof(WCHAR_T));	
	for(size_t i = 0; i < len && Source[i]; ++i)
		Dest[i] = (WCHAR_T)Source[i];			

	return len;
}
/*Конвертация wchar_t* -> char* */
size_t wchar_to_char(char * &Dest, const wchar_t* Source, size_t len) {
	if(len == 0) {//если размер задан, то и место уже зарезервировано
		len = wchar_len(Source) + 1;
		Dest = new char[len];
	}
	len = wcstombs(Dest, Source, len);
	Dest[len - 1] = '\0';
	return len;
}
/*Конвертация WCHAR_T** -> wchar_t* */
size_t WCHAR_to_wchar(wchar_t * &Dest, const WCHAR_T* Source, size_t len) {
	if(len==0){//если размер задан, то и место уже зарезервировано
		len = WCHAR_len(Source) + 1;
		Dest = new wchar_t[len];
	}
	memset(Dest, 0, len*sizeof(wchar_t));
	for(size_t i = 0; i < len && Source[i]; ++i)
		Dest[i] = (wchar_t)Source[i];

	return len;
}
/*Конвертация WCHAR_T** -> char* */
size_t WCHAR_to_char(char * &Dest, const WCHAR_T* Source, size_t len) {
	wchar_t * temp;
	WCHAR_to_wchar(temp, Source);
	len=wchar_to_char(Dest, temp,len);
	delete[] temp;

	return len;
}
/*Вычисление длинны строки WCHAR_T* */
size_t WCHAR_len(const WCHAR_T* Source) {
	size_t res = 0;
	while(Source[res]) 	++res;

	return res;
}
/*Вычисление длинны строки wchar_t* */
size_t wchar_len(const wchar_t* Source) {
	size_t res = 0;
	while(Source[res]) 	++res;

	return res;
}


/*Список доступных типов*/
static const wchar_t Class_Names[] = L"myClass"; //|OtherClass1|OtherClass2

/*ЭКСПОРТИРУЕМЫЕ МЕТОДЫ*/
/*Получение экземпляра по имени (регистрируются в RegisterExtensionAs тем же именем)*/
long GetClassObject(const WCHAR_T* ex_name, Base** pInterface) {
	if(!*pInterface) {
		wchar_t * name = nullptr;
		WCHAR_to_wchar(name, ex_name);
		if(!wcscmp(name, L"myClass"))
			*pInterface = new myClass();
		delete[] name;
		return (long)*pInterface;
	}
	return 0;
}
/*Уничтожение экземпляра*/
long DestroyObject(Base** pIntf) {
	if(!*pIntf)
		return -1;

	delete *pIntf;
	*pIntf = 0;
	return 0;
}
/*Получение списка возможных типов*/
const WCHAR_T* GetClassNames() {
	static WCHAR_T* names;
	wchar_to_WCHAR(names, Class_Names);

	return names;
}
Показать


Это base.h
/* Тута не писать! */

#ifndef BASE_H
#define BASE_H
#define _CRT_SECURE_NO_WARNINGS


#include "adapter.h"
#include "memory_adapter.h" 
#include "types.h" 
#include <cstring>
#include <functional>
using namespace std::placeholders;

/*интерфейс для инициализации компоненты (абстрактный класс)*/
class IInitDoneBase {
public:
	/*деструктор*/
	virtual ~IInitDoneBase() {}


	/* Инициализация
	*  @param disp - 1C:Enterpise interface
	*  @return the result of */
	virtual bool ADDIN_API Init(void* disp) = 0;


	/* Умтановка менеджера памяти
	*  @param mem - указатель на интерфейс менеджера памяти
	*  @return the result of */
	virtual bool ADDIN_API setMemManager(void* mem) = 0;


	/* Возврат версии компоненты
	*  @return - component version (2000 - это версия 2) */
	virtual long ADDIN_API GetInfo() = 0;


	/* Завершение работы компоненты.
	* Освобождение ресурсов.  */
	virtual void ADDIN_API Done() = 0;
};


/* Предопределенный интерфейс описания методов предназначенный для использования платформой 1С.
*  Интерфейс описывает расширение языка 1С*/
class ILanguageExtenderBase {
public:
	/*Деструктор*/
	virtual ~ILanguageExtenderBase() {}

	/* Имя класса в 1С
	*  @param ex_name - имя
	*  @return the result of  */
	virtual bool ADDIN_API RegisterExtensionAs(WCHAR_T** ex_name) = 0;

	/*==== СВОЙСТВА ====*/
	/* Возврат количества свойств класса, доступных из 1С
	*  @return количество свойств */
	virtual long ADDIN_API GetNProps() = 0;

	/* Определение номера свойства по имени
	*  @param ex_name - имя свойсва
	*  @return индекс свойства или -1, если не найдено */
	virtual long ADDIN_API FindProp(const WCHAR_T* ex_name) = 0;

	/* Возврат имени свойства по индексу
	*  @param num        - индекс свойства (начиная с 0)
	*  @param cur_locale - 0 - английское имя (обязательно должны быть),
	*                      1 - русское имя.
	*  @return имя свойства или 0, если не найдено */
	virtual const WCHAR_T* ADDIN_API GetPropName(long num, long cur_locale) = 0;

	/* Возврат значения свойства
	*  @param num - индекс свойства (начиная с 0)
	*  @param val - указатель на variable со значение свойства
	*  @return the result of   */
	virtual bool ADDIN_API GetPropVal(const long num, tVariant* val) = 0;

	/* Установка значения свойства
	*  @param num - индекс свойства (начиная с 0)
	*  @param val - указатель на variable хранящий новое значение
	*  @return the result of */
	virtual bool ADDIN_API SetPropVal(const long num, tVariant* val) = 0;

	/* Проверка возможности чтения значения свойсва
	*  @param num - индекс свойства (начиная с 0)
	*  @return true, если свойсво можно читать  */
	virtual bool ADDIN_API IsPropReadable(const long num) = 0;

	/* Проверка возможности изменять значение свойства
	*  @param num - индекс свойства (начиная с 0)
	*  @return true, если свойство можно изменить */
	virtual bool ADDIN_API IsPropWritable(const long num) = 0;


	/*==== МЕТОДЫ ====*/
	/* Возврат количества методов класса, доступных из 1С
	*  @return количество методов */
	virtual long ADDIN_API GetNMethods() = 0;

	/* Поиск метода по имени
	*  @param ex_name - имя метода
	*  @return             - индекс метода или -1, если не найдено  */
	virtual long ADDIN_API FindMethod(const WCHAR_T* ex_name) = 0;

	/* Возврат имени метода по индексу
	*  @param num        - индекс метода (начиная с 0)
	*  @param cur_locale - 0 - английское имя (обязательно должны быть),
	*                      1 - русское имя.
	*  @return имя метода или 0, если не найдено  */
	virtual const WCHAR_T* ADDIN_API GetMethodName(const long num, const long cur_locale) = 0;

	/* Возврат количества аргументов метода
	*  @param num - индекс метода (начиная с 0)
	*  @return количество аргументов  */
	virtual long ADDIN_API GetNParams(const long num) = 0;

	/* Возвращает значения поумолчанию аргументов метода
	*  @param lMethodNum - индекс метода (начиная с 0)
	*  @param lParamNum  - индекс параметра (начиная с 0)
	*  @param pvarParamDefValue - указатель на variable со значением поумолчанию
	*  @return the result of  */
	virtual bool ADDIN_API GetParamDefValue(const long lMethodNum, const long lParamNum, tVariant *pvarParamDefValue) = 0;

	/* Возвращает ли метод значение
	*  @param num - индекс метода (начиная с 0)
	*  @return true, если метод имеет возвращаемое значение */
	virtual bool ADDIN_API HasRetVal(const long num) = 0;

	/* Вызов метода без возвращаемого значения
	*  @param lMethodNum - индекс метода (начиная с 0)
	*  @param paParams   - указатель на массив аргументов метода
	*  @param lSizeArray - размер массива
	*  @return the result of   */
	virtual bool ADDIN_API CallAsProc(const long lMethodNum, tVariant* paParams, const long lSizeArray) = 0;

	/* Вызов метода с возвращаемым значением
	*  @param lMethodNum   - индекс метода (начиная с 0)
	*  @param pvarRetValue - указатель на возвращаемое значение
	*  @param paParams     - указатель на массив аргументов метода
	*  @param lSizeArray   - размер массива
	*  @return the result of  */
	virtual bool ADDIN_API CallAsFunc(const long lMethodNum, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) = 0;
};


/* Интерфейс используется, для изменения локализации в компоненте */
class LocaleBase {
public:
	/*Деструктор*/
	virtual ~LocaleBase() {}

	/* Изменение локализации
	*  @param loc - новая локаль (для Windows - rus_RUS, для Linux - ru_RU, и т.п...)   */
	virtual void ADDIN_API SetLocale(const WCHAR_T* loc) = 0;
};


/***************************************************
*  Данный класс предназначен для наследования      *
****************************************************/
class Base:public IInitDoneBase, public ILanguageExtenderBase, public LocaleBase {
protected:
	/*вложенные типы (для "рефлексии типа из 1С")*/
	struct Prop {	
		wchar_t *ru, *en;
		bool    r,w;
		void Init(const wchar_t *ru, const wchar_t *en, bool r, bool w) {
			/*копируем имена свойств ru и en*/
			size_t len = wcslen(ru) + 1;
			this->ru = new wchar_t[len];
			memcpy(this->ru, ru, len*sizeof(wchar_t));
			len = wcslen(en) + 1;
			this->en = new wchar_t[len];
			memcpy(this->en, en, len*sizeof(wchar_t));
			/*копируем призначки чтения и записи*/
			this->r = r;
			this->w = w;
		}
		Prop(const wchar_t *ru, const wchar_t *en, bool r, bool w) {
			Init(ru, en, r, w);
		}
		Prop(const Prop & other) {
			Init(other.ru, other.en, other.r, other.w);
		}
		~Prop() {
			delete[] ru, en;
			ru = en = nullptr;
		}
	};
	struct Method {
		typedef bool(_stdcall *func_t)(tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);
		typedef bool(_stdcall *proc_t)(tVariant* paParams, const long lSizeArray);
		wchar_t *ru, *en;
		bool    is_proc;
		short   cnt;

		func_t func; //указатель на функцию
		proc_t proc; //указатель на процедуру

		void Init(const wchar_t *ru, const wchar_t *en, func_t func, proc_t proc, short cnt, bool is_proc) {
			/*копируем имена методов ru и en*/
			size_t len = wcslen(ru) + 1;
			this->ru = new wchar_t[len];
			memcpy(this->ru, ru, len*sizeof(wchar_t));
			len = wcslen(en) + 1;
			this->en = new wchar_t[len];
			memcpy(this->en, en, len*sizeof(wchar_t));
			/*копируем указатели на функию и процедуру*/
			this->func = func;
			this->proc = proc;
			/*копируем количество параметров*/
			this->cnt = cnt;
			/*копируем признак процедуры*/
			this->is_proc = is_proc;
			
		}
		Method(const wchar_t *ru, const wchar_t *en, void * func, void * proc = nullptr, short cnt = 0, bool is_proc = false) {
			Init(ru, en, func != nullptr ? *(Base::Method::func_t*)func : nullptr, proc!=nullptr ? *(Base::Method::proc_t*)proc : nullptr, cnt, is_proc);
		}
		Method(const Method & other) {
			Init(other.ru, other.en, other.func, other.proc, other.cnt, other.is_proc);
		}
		~Method() {
			delete[] ru, en;
			ru = en = nullptr;
		}
	};
	
	/*адаптеры*/
	Adapter            * pAdapter;
	MemoryAdapter      * pMemoryAdapter;
	
	/*Локальное имя*/
	wchar_t * name;
	/*свойства, методы и классы*/
	long cnt_props;
	Prop ** Props;	
	long cnt_methods;
	Method ** Methods;
	/*заполнение свойств и методов*/
	void fill_name(const wchar_t * name);
	void fill_props(const Prop * Prors = nullptr, long cnt_props = 0);
	void fill_methods(const Method * Methods = nullptr, long cnt_methods = 0);
		
	virtual void addError(uint32_t wcode, const wchar_t* source, const wchar_t* descriptor, long code);

public:
	/*Деструктор*/
	virtual ~Base();
	/*Конструктор*/
	Base():cnt_props(0), Props(nullptr), cnt_methods(0), Methods(nullptr), pAdapter(nullptr), pMemoryAdapter(nullptr), name(nullptr) {}

	/*Инициализация и завершение*/
	virtual bool ADDIN_API Init(void*)                   override;
	virtual bool ADDIN_API setMemManager(void* mem)      override;
	virtual long ADDIN_API GetInfo()                     override;
	virtual void ADDIN_API Done()                        override;
	/*смена локализации*/
	virtual void ADDIN_API SetLocale(const WCHAR_T* loc) override;

	/*Расширение языка*/
	/*Регистрация расширения под конкретным именем*/
	virtual       bool     ADDIN_API RegisterExtensionAs(WCHAR_T**) override;

	virtual       long     ADDIN_API GetNProps() override  { return cnt_props; }
	virtual       long     ADDIN_API FindProp(const WCHAR_T* ex_name) override;
	virtual const WCHAR_T* ADDIN_API GetPropName(long num, long cur_locale) override;
	virtual       bool     ADDIN_API IsPropReadable(const long num) override;
	virtual       bool     ADDIN_API IsPropWritable(const long num) override;
	virtual       bool     ADDIN_API GetPropVal(const long num, tVariant* var) override { return false; } //заглушка на случай отсутствия свойств
	virtual       bool     ADDIN_API SetPropVal(const long num, tVariant* val) override { return false; } //заглушка на случай отсутствия свойств
	
	virtual       long     ADDIN_API GetNMethods() override  { return cnt_methods; }
	virtual       long     ADDIN_API FindMethod(const WCHAR_T* ex_name) override;
	virtual const WCHAR_T* ADDIN_API GetMethodName(const long num, const long cur_locale) override;
	virtual       long     ADDIN_API GetNParams(const long num) override;
	virtual       bool     ADDIN_API GetParamDefValue(const long num, const long lParamNum, tVariant *pvarParamDefValue) override { return false; }           //заглушка на случай отсутствия методов
	virtual       bool     ADDIN_API HasRetVal(const long num) override;
	virtual       bool     ADDIN_API CallAsProc(const long num, tVariant* paParams, const long lSizeArray);
	virtual       bool     ADDIN_API CallAsFunc(const long num, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray);                           //заглушка на случай отсутствия методов
};


size_t wchar_to_WCHAR(WCHAR_T* &Dest, const wchar_t* Source, size_t len = 0);
size_t wchar_to_char(WCHAR_T * &Dest, const wchar_t* Source, size_t len = 0);
size_t WCHAR_to_wchar(wchar_t* &Dest, const WCHAR_T* Source, size_t len = 0);
size_t WCHAR_to_char(char * &Dest, const WCHAR_T* Source, size_t len = 0);
size_t WCHAR_len(const WCHAR_T* Source);
size_t wchar_len(const wchar_t* Source);

/* These functions should be implemented that component can be loaded and created. */
extern "C" long GetClassObject(const WCHAR_T*, Base** pIntf);
extern "C" long DestroyObject(Base** pIntf);
extern "C" const WCHAR_T* GetClassNames();

/*указатели на функции*/
typedef long(*GetClassObjectPtr)(const WCHAR_T* wsName, Base** pIntf);
typedef long(*DestroyObjectPtr)(Base** pIntf);
typedef const WCHAR_T* (*GetClassNamesPtr)();

#endif 
Показать
16. ture 606 15.03.16 18:57 Сейчас в теме
(12) TSSV, пример 1С, который я прокомментировал и вставил в архив, то же можно использовать. Однако пример 1С предназначен только для ознакомления и выяснения принципа взаимодействия. Ну я совсем все карты уже раскрыл.

18. ture 606 15.03.16 19:07 Сейчас в теме
(12) TSSV, выше я привел другую реализацию базового класса и вообще много переделал.
Идея простая:
1) пишем имя класса
/*объявляем имя класса доступное из 1С*/
        Base::fill_name(L"myClass");

2) указываем свойства
/*объявляем свойства доступные из 1С*/
        Base::Prop Props[] = {
            {L"Свойство_int"   , L"Prorp0", true, true},
            {L"Свойство_double", L"Prorp1", true, true},
            {L"Свойство_pchar" , L"Prorp2", true, true},
            {L"Свойство_bool"  , L"Prorp3", true, true},
            {L"Свойство_tm"    , L"Prorp4", true, true}
        };

русское, английское, можно читать, можно менять
3) доступ к свойствам
virtual bool ADDIN_API GetPropVal(const long num, tVariant* var) override {....
virtual bool ADDIN_API SetPropVal(const long num, tVariant * var) override {

4) указываем методы
/*объявляем методы доступные из 1С*/
        Base::Method Methods[] = {
            {L"Функция1", L"Func1", nullptr, nullptr, 1},
            {L"Функция2", L"Func2en", (void*)&std::bind(&myClass::Func2, this, _1, _2), nullptr, 2},
            {L"Процедура1", L"Proc1en", nullptr, (void*)&std::bind(&myClass::Proc1, this, _1, _2), 1, true}
        };        

русское, английское, указатель на функцию, указатель на процедуру, признак процедуры
(void*)&std::bind(&myClass::Proc1, this, _1, _2) здесь надо менять только название Proc1
(функцию еще не тестировал сам)
5) реализация процедур и функций
bool Proc1(tVariant* paParams, const long lSizeArray) {
....      
  return true; 
    }
    bool Func2(tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) {
....
        return true;
    }
20. TSSV 1144 15.03.16 22:45 Сейчас в теме
14. ture 606 15.03.16 18:53 Сейчас в теме
Это base.cpp
#include "base.h"
#include <wchar.h>
#include <iostream>
#include <string>



/*Реализация базового абстрактного класса*/
void Base::fill_name(const wchar_t * name) {
	/*копируем имя*/
	size_t len = wcslen(name) + 1;
	this->name = new wchar_t[len];
	memcpy(this->name, name, len*sizeof(wchar_t));
}
void Base::fill_props(const Prop * Props, long cnt_props) {
	/*заполним возможные свойства, если они есть*/
	if(cnt_props) {
		this->cnt_props = cnt_props;
		this->Props     = new Prop*[cnt_props];
		for(long i = 0; i < cnt_props; ++i)
			this->Props[i] = new Prop(Props[i]);
	}
}
void Base::fill_methods(const Method * Methods, long cnt_methods) {
	/*заполним возможные методы, если они есть*/
	if(cnt_methods) {
		this->cnt_methods = cnt_methods;
		this->Methods = new Method*[cnt_methods];
		for(long i = 0; i < cnt_methods; ++i)
			this->Methods[i] = new Method(Methods[i]);
	}
}

Base::~Base() {
	/*освобождаем массивы имен свойств*/
	for(long i = 0; i < cnt_props; ++i)
		delete Props[i];
	delete[] Props;
	Props = nullptr;
	cnt_props = 0;

	/*освобождаем массивы имен методов*/
	for(long i = 0; i < cnt_methods; ++i)
		delete Methods[i];
	delete[] Methods;
	Methods = nullptr;
	cnt_methods = 0;

	delete[] name;
}


bool Base::Init(void* pConnection) {
	pAdapter = (Adapter*)pConnection;
	return pAdapter != nullptr;
}
bool Base::setMemManager(void* mem) {
	pMemoryAdapter = (MemoryAdapter*)mem;
	return pMemoryAdapter != 0;
}
long Base::GetInfo() {
	/*Компонента должна сообщить свою версию*/
	return 2000;
}
void Base::Done() {}

void Base::SetLocale(const WCHAR_T* loc) {
	#ifndef __linux__
		_wsetlocale(LC_ALL, loc);
	#else
		//We convert in char* char_locale
		//also we establish locale
		//setlocale(LC_ALL, char_locale);
	#endif
}

bool Base::RegisterExtensionAs(WCHAR_T** ext_name) {
	if(pMemoryAdapter!=nullptr) {
		int len = wcslen(name) + 1;
		if(pMemoryAdapter->AllocMemory((void**)ext_name, len * sizeof(WCHAR_T)))
			wchar_to_WCHAR(*ext_name, name, len);
		return true;
	}else
		return false;
}

/*"Рефлексия" из 1С*/
long Base::FindProp(const WCHAR_T* ex_name) {
	long res = -1;
	wchar_t * temp = nullptr;
	WCHAR_to_wchar(temp, ex_name);
		
	for(long i = 0; res == -1 && i < cnt_props; ++i)
		if(!wcscmp(Props[i]->en, temp) || !wcscmp(Props[i]->ru, temp))
			res = i;
	
	delete[] temp;

	return res;
}
bool Base::IsPropReadable(const long num) {
	if(num < cnt_props)
		return Props[num]->r;
	else
		return false;
}
bool Base::IsPropWritable(const long num) {
	if(num < cnt_props)
		return Props[num]->w;
	else
		return false;
}
const WCHAR_T* Base::GetPropName(long num, long cur_locale) {
	if(num < cnt_props) {
		wchar_t * temp;
		if(cur_locale == 0)
			temp=Props[num]->en;
		else 
			temp = Props[num]->ru;		
		
		WCHAR_T * res_name = NULL;
		int len = wcslen(temp) + 1;
		
		if(pMemoryAdapter 
		   && temp
		   && pMemoryAdapter->AllocMemory((void**)&res_name, len*sizeof(WCHAR_T)))
				wchar_to_WCHAR(res_name, temp, len);
		
		return res_name;
	} else
		return nullptr;
}
long Base::FindMethod(const WCHAR_T* ex_name) {
	long res = -1;
	wchar_t * temp = nullptr;
	WCHAR_to_wchar(temp, ex_name);

	for(long i = 0; res == -1 && i < cnt_methods; ++i)
		if(!wcscmp(Methods[i]->en, temp) || !wcscmp(Methods[i]->ru, temp))
			res = i;

	delete[] temp;

	return res;
}
bool Base::HasRetVal(const long num) {
	if(num < cnt_methods)
		return Methods[num]->is_proc == false;
	else
		return false;
}
const WCHAR_T* Base::GetMethodName(const long num, const long cur_locale) {
	if(num < cnt_methods) {
		wchar_t * temp;
		if(cur_locale == 0)
			temp = Methods[num]->en;
		else
			temp = Methods[num]->ru;

		WCHAR_T * res_name = NULL;
		int len = wcslen(temp) + 1;

		if(pMemoryAdapter
		   && temp
		   && pMemoryAdapter->AllocMemory((void**)&res_name, len*sizeof(WCHAR_T)))
		   wchar_to_WCHAR(res_name, temp, len);

		return res_name;
	} else
		return nullptr;
}
long Base::GetNParams(const long num) {
	if(num < cnt_methods)
		return Methods[num]->cnt;
	else
		return 0;
}

void Base::addError(uint32_t wcode, const wchar_t* source, const wchar_t* descriptor, long code) {
	if(pAdapter) {
		WCHAR_T *err   = nullptr;
		WCHAR_T *descr = nullptr;

		wchar_to_WCHAR(err, source);
		wchar_to_WCHAR(descr, descriptor);

		pAdapter->AddError(wcode, err, descr, code);
		delete[] err, descr;
	}
}

bool Base::CallAsProc(const long num, tVariant* paParams, const long lSizeArray) { 
	if(num < cnt_methods && Methods[num]->proc != nullptr) {		
		return Methods[num]->proc(paParams, lSizeArray);		
	} else
		return false; 
}

bool Base::CallAsFunc(const long num, tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) {
	if(num < cnt_methods && Methods[num]->func != nullptr) {
		return Methods[num]->func(pvarRetValue, paParams, lSizeArray);
	} else
		return false;
}
Показать
15. ture 606 15.03.16 18:54 Сейчас в теме
Это пример реализации class.hpp:
#include "base.h"
#include <ctime>

#ifndef CLASS_H
#define CLASS_H

class myClass: public Base {
private:
	int     Prop0 = -113; 
	double  Prop1 = 7.65;
	char  * Prop2 = nullptr;
	bool    Prop3 = true;
	tm      Prop4;
public:
	/*конструктор*/
	myClass() {
		/*объявляем имя класса доступное из 1С*/
		Base::fill_name(L"myClass");

		/*объявляем свойства доступные из 1С*/
		Base::Prop Props[] = {
			{L"Свойство_int"   , L"Prorp0", true, true},
		    {L"Свойство_double", L"Prorp1", true, true},
			{L"Свойство_pchar" , L"Prorp2", true, true},
			{L"Свойство_bool"  , L"Prorp3", true, true},
			{L"Свойство_tm"    , L"Prorp4", true, true}
		};
		Base::fill_props(Props, sizeof(Props) / sizeof(Base::Prop));

		/*объявляем методы доступные из 1С*/
		Base::Method Methods[] = {
			{L"Функция1", L"Func1", nullptr, nullptr, 1},
			{L"Функция2", L"Func2en", (void*)&std::bind(&myClass::Func2, this, _1, _2), nullptr, 2},
			{L"Процедура1", L"Proc1en", nullptr, (void*)&std::bind(&myClass::Proc1, this, _1, _2), 1, true}
		};		
		Base::fill_methods(Methods, sizeof(Methods) / sizeof(Base::Method));
		
		Prop2 = new char[100];
		std::strcpy(Prop2, "abc абС 123");

		time_t rawtime;
		time(&rawtime);
		Prop4 = *localtime(&rawtime);		
	}
	~myClass() { 
		delete[] Prop2; 		
	}
	
	/*Получение свойства*/
	virtual bool ADDIN_API GetPropVal(const long num, tVariant* var) override {
		switch(num) {
			case 0: //Свойство_int
				TV_VT(var) = VTYPE_I4; //выставляем тип
				TV_I4(var) = Prop0;    //выставляем значение
				break;
			case 1: //Свойство_double
				TV_VT(var) = VTYPE_R8;  //выставляем тип
				TV_R8(var) = Prop1;     //выставляем значение
				break;
			case 2: //Свойство_pchar
				TV_VT(var) = VTYPE_PSTR;  //выставляем тип
				var->pstrVal = Prop2;     //сразу указатель на строку
				var->strLen  = std::strlen(Prop2);
				break;
			case 3: //Свойство_bool
				TV_VT(var) = VTYPE_BOOL; //выставляем тип
				TV_BOOL(var) = Prop3;    //выставляем значение
				break;
			case 4: //Свойство_tm
				TV_VT(var) = VTYPE_TM; //выставляем тип
				var->tmVal = Prop4;    //выставляем значение
				break;
			default:
				return false;
		}
		return true;
	}
	/*Установка свойства*/
	virtual bool ADDIN_API SetPropVal(const long num, tVariant * var) override {
		switch(num) {
			case 0:
				if(TV_VT(var) != VTYPE_I4)
					return false;
				Prop0 = TV_I4(var);
				break;
			case 1:
				if(TV_VT(var) != VTYPE_R8)
					return false;
				Prop1 = TV_R8(var);
				break;
			case 2:
				if(TV_VT(var) == VTYPE_PSTR) { 
					delete[] Prop2;
					size_t len = std::strlen(var->pstrVal);
					Prop2 = new char[len + 1];
					std::strncpy(Prop2, var->pstrVal, len + 1);
					break;
				} else if(TV_VT(var) == VTYPE_PWSTR) {
					delete[] Prop2;
					WCHAR_to_char(Prop2, var->pwstrVal);
					break;
				} else
					return false;
			case 3:
				if(TV_VT(var) != VTYPE_BOOL)
					return false;
				Prop3 = TV_BOOL(var);
				break;
			case 4:
				if(TV_VT(var) != VTYPE_TM)
					return false;
				Prop4 = var->tmVal;
				break;
			default:
				return false;
		}
		return true;
	}
	/*Методы*/
	bool Proc1(tVariant* paParams, const long lSizeArray) {
		return true; 
	}
	bool Func2(tVariant* pvarRetValue, tVariant* paParams, const long lSizeArray) {
		return true;
	}
};

#endif
Показать
17. ture 606 15.03.16 18:58 Сейчас в теме
(15) ture, этот метод
{L"Функция1", L"Func1", nullptr, nullptr, 1},
не реализован.
19. ture 606 15.03.16 19:09 Сейчас в теме
Я еще работаю над реализацией базового класса.
23. MherArsh 24 16.03.16 09:22 Сейчас в теме
Привет всем!

В принципе ничего сложного, я тоже когда то делал так, писал компоненту для работы с картинками.

Могу дать один совет начинающим, используйте C++Builder (Embarcadero) вместо Visual C++, там реально дохрена всяких классов под любое требование, начиная с простого до веб сервисов например .
24. Ivon 673 16.03.16 11:55 Сейчас в теме
Вроде бы все красиво и понятно, но в заголовке нужно добавить "для С++". Например я С++ не знаю, но знаю C#. При этом должен отметить, что подобные внешние компоненты, которые не интегрируются в интерфейс 1С, не так сложно начать писать и на C#. Мне удалось собрать по крупицам информацию, достаточную для написания интерфейсных ВК и даже написать такую ВК для собственных нужд (чтение изображения из базы MSSQL и отображение этого изображения в самой компоненте, так как поле картинки в 1С при смене картинки в поле не освобождает память, в итоге клиент можно крашануть просто меняя в поле 2 картинки одну на другую 100500 раз), но процесс очень сложно назвать легким для понимания. Поэтому я отслеживаю подобные статьи в надежде на поиск нормальной инструкции. Сам инструкцию пока написать не могу, так как некоторые манипуляции делаются без понимания, зачем они нужны, но со знанием, что если этого не сделать, то ВК не заработает.
invertercant; +1 Ответить
25. skyadmin 56 17.03.16 13:04 Сейчас в теме
(24) Ivon, аналогично сдлал, только на VB.NET.
Если бы был пример на C#, то можно было бы через Convert .NET преобразовать C# в VB.NET, попробовать.
26. ture 606 17.03.16 13:44 Сейчас в теме
(25) skyadmin, пример на c#?
В прошлом году возился с шарпом. Все компоненты через COM требуют регистрации. С админами не договориться без ружья на медведя... Чуть что серьезное и на шарпе нету, надо собирать dll из исходников на с/с++/java.... а то и вообще выковыривать из опенсорс под линукс. Короче говоря, шарп решил бросить.
27. talych 17 17.03.16 16:12 Сейчас в теме
Есть внешняя компонента, написанная для 1С 8.1 В ней использовал CDialogEx
Можно ли использовать их в Native API? Клиенты работают только на винде. Пытаюсь выполнить код, вылетает в assert
		if(!m_pDlg)
			{
				m_pDlg = new CMainDlg();
				m_pDlg->Create(IDD_DIALOG2);
			}
29. ture 606 22.03.16 18:27 Сейчас в теме
Чуток промазал в исходниках. Правильней так.

size_t wchar_to_char(char * &Dest, const wchar_t* Source, size_t len) {
	/*if(len == 0) {//если размер задан, то и место уже зарезервировано
		len = wchar_len(Source) +1;
		Dest = new char[len];
	}
	wcstombs(Dest, Source, len);
	Dest[len - 1] = '\0';*/
	
	if(len == 0) {
		len = WideCharToMultiByte(
			1251,   // Code page
			0,      // Default replacement of illegal chars
			Source, // Multibyte characters string
			-1,     // Number of unicode chars is not known
			NULL,   // No buffer yet, allocate it later
			0,      // No buffer
			NULL,   // Use system default
			NULL    // We are not interested whether the default char was used
			);
		if(len == 0)
			return 0;
		else
			Dest = new char[len]; 
	}

	len = WideCharToMultiByte(
		1251,    // Code page
		0,       // Default replacement of illegal chars
		Source,  // Multibyte characters string
		-1,      // Number of unicode chars is not known
		Dest,    // Output buffer
		len,     // buffer size
		NULL,    // Use system default
		NULL     // We are not interested whether the default char was used
		);

	if(len == 0) {
		delete[] Dest;
		return 0;
	}

	return len;
}
Показать
30. so-quest 140 27.03.16 23:48 Сейчас в теме
Примерно представляя сколько труда надо вложить в статью, говорю - спасибо. Написано немного сумбурно, но читаемо. Единственное что хотел бы добавить - если не стоит цели заработать старманей, то стоит подумать об использовании github или ему подобных серверов.

31. ture 606 29.03.16 15:40 Сейчас в теме
(30) so-quest, нет цели зарабатывать монетки. Я, как и все, складирую полезные вещи с описанием. В какой-то момент статья была в тренде... пришлось поподробней и побольше написать.
32. ture 606 30.03.16 14:57 Сейчас в теме
На сервачке:
	Объект_=РеквизитФормыВЗначение("Объект");			
			адрес = ПоместитьВоВременноеХранилище(Объект_.ПолучитьМакет("SuperI"));
			res=ПодключитьВнешнююКомпоненту(адрес,"VK",ТипВнешнейКомпоненты.Native);			
			obj = Новый("AddIn.VK.myClass");
33. ture 606 30.03.16 18:47 Сейчас в теме
На клиенте:
&НаСервере
Функция GetInf()
	Перем стРезультат;
	
	oSQL=Объект.Код;
	бРезультат=ВыполнитьSQL(oSQL,"select 1 as [q]");
	Если бРезультат тогда 		
		Объект_ = РеквизитФормыВЗначение("Объект");
		стРезультат=Новый Структура("Адрес,Сервер,База,Пользователь,Пароль",
									ПоместитьВоВременноеХранилище(Объект_.ПолучитьМакет("Макет_v2"),Новый УникальныйИдентификатор),
		                            oSQL.Server,
									oSQL.Base,									
									"***","***");		
	КонецЕсли;
	
	Возврат стРезультат;
КонецФункции	

&НаКлиенте
Процедура xmlНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
	Перем Criterion;
	
	СтандартнаяОбработка=Ложь;
	стРезультат=GetInf();
	Если стРезультат<>Неопределено тогда 
		Попытка 
			res=ПодключитьВнешнююКомпоненту(стРезультат.Адрес, "VK", ТипВнешнейКомпоненты.Native);					
			Criterion = Новый("AddIn.VK.Criterion");			
		Исключение
			УстановитьВнешнююКомпоненту(стРезультат.Адрес);
			res=ПодключитьВнешнююКомпоненту(стРезультат.Адрес, "VK", ТипВнешнейКомпоненты.Native);					
			Criterion = Новый("AddIn.VK.Criterion");		
		КонецПопытки;
		УдалитьИзВременногоХранилища(стРезультат.Адрес);
		
		Criterion.Сервер=стРезультат.Сервер;
		Criterion.База  =стРезультат.База;
		Criterion.КритерийОтбора ="";
		Criterion.ПользовательSQL=стРезультат.Пользователь;
		Criterion.ПарольSQL      =стРезультат.Пароль;
		Объект.xml=Criterion.ПолучитьXMLфайл();
	КонецЕсли;
КонецПроцедуры
Показать


В макет складываем манифест и все прописанные в нем dll (достаточно под 32 и 64 бита на ведре)
34. IgorLee 18.06.16 09:18 Сейчас в теме
Всем привет !

(1С8.3.5.1625)

Кто либо сталкивался с проблемой, что компонента "сама выгружается" от 1С или 1С её выгружает сама без какой либо причины ?

Т.е. - загружаю компоненту, к примеру в форме дока (сохраняется в переменной формы дока), далее проходит время (минут 15 и более бывает) и компонента "выгружается" от 1С8.

Примечание: в компоненту добавил логирование, соотв. из логов делаю вывод
35. IgorLee 18.06.16 14:59 Сейчас в теме
(34) IgorLee,

В общем вроде разобрался.

Дело вот в чем - 1С8 сама выгружает компоненту ровно через 20 минут, проверял кучу раз (расхождение не более 15 сек. между "опытами") !

Но что я раскопал...
Оказывается если у "IAddInDefBaseEx" запросить интерфейс "IMsgBox" и/или "IPlatformInfo" через метод "GetInterface(...)" - и хранить значение этого интерфейса, к примеру, в локальной переменной класса компоненты - то 1С не выгружает Вашу компоненту !

Оч. странно вышло :(
36. ture 606 20.06.16 11:51 Сейчас в теме
(35) IgorLee, меня тут в sap окунули с головой. И как-то склоняться я к мысли стал, что 1С не игрок на мировом рынке. Больше того 1С не игрок и на своем рынке после того свинства с переходом с 1С7.7 к 1С8. Я имею ввиду то, что пришлось не просто код программ целиком переписывать, а даже самих программистов переучивать. Теперь старожилы 1С не сильно вникают в тонкости и детали платформы, потому что знают что однажды всё снова изменится и заплатят они дорого за своё "тайное" и выстраданное знание, не желая от него отказаться. В итоге в 1С остаются только лошки, которые и программистами называют себя с натяжкой и знают, что доход их складывается от знания бредовой реализации типовых конфигураций, а не программирования и знания внутреннего языка. Такого в SAP не было сколько мне известно. Вот такие вот тонкости.
37. vadim1980 131 22.08.21 16:39 Сейчас в теме
Вас не удивляет преобразование return (long)*pInterface в GetClassObject на 64-битной платформе? Или они всегда подгружают компоненты в адресное пространство < 4Гб?
38. user1603200 18.02.22 18:45 Сейчас в теме
Непонятно зачем приводить исходный код шаблона компоненты. Гораздо полезнее были бы реальные примеры.
Оставьте свое сообщение