gifts2017

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

Опубликовал Женька Ture (ture) в раздел Программирование - Внешние компоненты

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

Запускаем 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 не требует регистрации? 

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

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

См. также

Подписаться Добавить вознаграждение

Комментарии

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

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

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

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

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

Я столкнулся с тем, что проект на ИТС написан ровно для того, чтобы быстро разобраться как и что. Но он не может служить основой реальной разработки. Я решил исправить недочет и стал выкладывать решение последовательно. В какой-то момент свое решение я стал переписывать в третий раз и уже с учетом большинства своих хочу. Но версию с фрагментами ИТС решил выложить, чтоб не пропадало. Сейчас я готовлю рабочий проект с базовым классом, который удобно и просто наследовать (собственно пишу с самого начала для себя, чтоб удобней было быстро стартовать).
5. Женька Ture (ture) 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 (ture) 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 (ture) 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 (ture) 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 (ture) 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
...Показать Скрыть

Я еще работаю над приятном видом. Что-то будет меняться.
10. Андрей Овсянкин (Evil Beaver) 15.03.16 13:33
(4) ture, нет никакого "моего" варианта. Я просто документировал тот шаблон, который лежит на ИТС.
А вопрос мой был про то - что из статьи непонятно, что именно лежит в архиве. Отличается ли оно от ИТС-ного шаблона и в какую сторону.
11. Женька Ture (ture) 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"

Я не добавлял в архив человеческую реализацию базового класса, который можно легко наследовать. Я только работаю над ним, но что-то выложил в комментариях уже.
12. Сергей (TSSV) 15.03.16 18:24
"Я не добавлял в архив человеческую реализацию базового класса, который можно легко наследовать." Как использовать скачанный архив, посоветуйте пожалуйста.
13. Женька Ture (ture) 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 
...Показать Скрыть
14. Женька Ture (ture) 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 (ture) 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
...Показать Скрыть
16. Женька Ture (ture) 15.03.16 18:57
(12) TSSV, пример 1С, который я прокомментировал и вставил в архив, то же можно использовать. Однако пример 1С предназначен только для ознакомления и выяснения принципа взаимодействия. Ну я совсем все карты уже раскрыл.

17. Женька Ture (ture) 15.03.16 18:58
(15) ture, этот метод
{L"Функция1", L"Func1", nullptr, nullptr, 1},
не реализован.
18. Женька Ture (ture) 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;
    }
...Показать Скрыть
19. Женька Ture (ture) 15.03.16 19:09
Я еще работаю над реализацией базового класса.
20. Сергей (TSSV) 15.03.16 22:45
21. Сергей Лесовой (Synoecium) 16.03.16 06:27
(3) Evil Beaver, Ваша статья идеально подходит для новичков, которым надо разобраться как создавать внешние компоненты. Здесь же написан какой-то сумбур. Вроде я понимаю о чем речь, но как то смутно, при этом я сам ковырялся с внешними компонентами. В общем автору пока минус, в надежде на то, что он выложит волшебный класс, про который много говорит. За что 10 sm вообще не понятно.
22. Женька Ture (ture) 16.03.16 08:51
(21) Synoecium, всегда приятно читать такие комменты, они расслабляют и позволяют не напрягаться. Компонент я выложил в своих комментах.
23. Маским Константинович (MherArsh) 16.03.16 09:22
Привет всем!

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

Могу дать один совет начинающим, используйте C++Builder (Embarcadero) вместо Visual C++, там реально дохрена всяких классов под любое требование, начиная с простого до веб сервисов например .
24. Ivon (Ivon) 16.03.16 11:55
Вроде бы все красиво и понятно, но в заголовке нужно добавить "для С++". Например я С++ не знаю, но знаю C#. При этом должен отметить, что подобные внешние компоненты, которые не интегрируются в интерфейс 1С, не так сложно начать писать и на C#. Мне удалось собрать по крупицам информацию, достаточную для написания интерфейсных ВК и даже написать такую ВК для собственных нужд (чтение изображения из базы MSSQL и отображение этого изображения в самой компоненте, так как поле картинки в 1С при смене картинки в поле не освобождает память, в итоге клиент можно крашануть просто меняя в поле 2 картинки одну на другую 100500 раз), но процесс очень сложно назвать легким для понимания. Поэтому я отслеживаю подобные статьи в надежде на поиск нормальной инструкции. Сам инструкцию пока написать не могу, так как некоторые манипуляции делаются без понимания, зачем они нужны, но со знанием, что если этого не сделать, то ВК не заработает.
invertercant; +1 Ответить 2
25. Андрей Кокконен (skyadmin) 17.03.16 13:04
(24) Ivon, аналогично сдлал, только на VB.NET.
Если бы был пример на C#, то можно было бы через Convert .NET преобразовать C# в VB.NET, попробовать.
26. Женька Ture (ture) 17.03.16 13:44
(25) skyadmin, пример на c#?
В прошлом году возился с шарпом. Все компоненты через COM требуют регистрации. С админами не договориться без ружья на медведя... Чуть что серьезное и на шарпе нету, надо собирать dll из исходников на с/с++/java.... а то и вообще выковыривать из опенсорс под линукс. Короче говоря, шарп решил бросить.
27. Талыч Sha (talych) 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 (ture) 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) 27.03.16 23:48
Примерно представляя сколько труда надо вложить в статью, говорю - спасибо. Написано немного сумбурно, но читаемо. Единственное что хотел бы добавить - если не стоит цели заработать старманей, то стоит подумать об использовании github или ему подобных серверов.

31. Женька Ture (ture) 29.03.16 15:40
(30) so-quest, нет цели зарабатывать монетки. Я, как и все, складирую полезные вещи с описанием. В какой-то момент статья была в тренде... пришлось поподробней и побольше написать.
32. Женька Ture (ture) 30.03.16 14:57
На сервачке:
	Объект_=РеквизитФормыВЗначение("Объект");			
			адрес = ПоместитьВоВременноеХранилище(Объект_.ПолучитьМакет("SuperI"));
			res=ПодключитьВнешнююКомпоненту(адрес,"VK",ТипВнешнейКомпоненты.Native);			
			obj = Новый("AddIn.VK.myClass");
33. Женька Ture (ture) 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. Igor Lee (IgorLee) 18.06.16 09:18
Всем привет !

(1С8.3.5.1625)

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

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

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

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

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

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

Оч. странно вышло :(
36. Женька Ture (ture) 20.06.16 11:51
(35) IgorLee, меня тут в sap окунули с головой. И как-то склоняться я к мысли стал, что 1С не игрок на мировом рынке. Больше того 1С не игрок и на своем рынке после того свинства с переходом с 1С7.7 к 1С8. Я имею ввиду то, что пришлось не просто код программ целиком переписывать, а даже самих программистов переучивать. Теперь старожилы 1С не сильно вникают в тонкости и детали платформы, потому что знают что однажды всё снова изменится и заплатят они дорого за своё "тайное" и выстраданное знание, не желая от него отказаться. В итоге в 1С остаются только лошки, которые и программистами называют себя с натяжкой и знают, что доход их складывается от знания бредовой реализации типовых конфигураций, а не программирования и знания внутреннего языка. Такого в SAP не было сколько мне известно. Вот такие вот тонкости.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа