Перейти к содержимому


Разработка внешних компонент на .NET (C#) для 1С 8.2


Сообщений в теме: 19

#1 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 21 февраля 2012 - 08:55

Доброго времени суток.
Суть проблемы следующая:

Для решения поставленной задачи необходимо разработать внешнюю компоненту (.dll) (.NET C# VS08) и обрабатывать в 1С (8.2) внешнее событие, генерируемое этой компонентой.
С регистрацией компоненты в Windows с помощью RegAsm.exe проблем нет (в реестр добавляется запись, классы компоненты там есть).
Подключение компоненты в 1С по ProgID тоже проходит успешно, а вот при попытке создать экземпляр - выдает ошибку следующего содержания "{Обработка.ТестВнешнейКомпоненты.Форма.Форма.Форма(56)}: Тип не определен (AddIn.TestDLL3)".

Мною изучено:
http://www.gotdotnet...sanshin-R/6626/
http://www.gotdotnet...forums/2/53505/
http://www.forum.mis....php?id=436704/

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

Может кто знает, какие подводные камни могут встретиться в подобных разработках? В чём может быть моя проблема ? Или, хотя бы, приведите, пожалуйста, работоспособные примеры разработанных внешних компонент.
Буду очень благодарен любой помощи или консультации.
Заранее, спасибо!

#2 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 21 февраля 2012 - 09:30

С внешними компонентами не работал.
Как подключаете?
Не было ли ничего полезного в гугле при поиске по словам "Тип не определен (AddIn."?
Now, this bell tolling softly for another, says to me: Thou must die...

#3 vartanet

    Ветеран


  • 1 497 сообщений

Отправлено 21 февраля 2012 - 10:21

на диске ИТС есть статья по созданию внешних компонент.. может поможет..
Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете.

#4 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 22 февраля 2012 - 10:02

Сначала регистрирую в реестре с помощью
[CODE]
RegAsm.exe TestDLL3.dll /codebase /tlb: TestDLL3.tlb
@pause
[/CODE]

Потом испльзую обработку(не внешнюю), функция выглядит:
&НаКлиенте
Процедура Подключить()
Попытка
  Если ПодключитьВнешнююКомпоненту("AddIn.TestDLL3") = 1 Тогда
   //Plugin = Новый ("AddIn.TestDLL3");
		    Предупреждение("Component AddIn.TestDLL3 successfuly loaded");
	    Иначе
		 Предупреждение("Component AddIn.TestDLL3 not found");
	    КонецЕсли;
    Исключение	  
		    Предупреждение("Error while loading AddIn.TestDLL3");
    КонецПопытки;
КонецПроцедуры 

Тут всё работает успешно, функции интерфейса IInitDone() отрабатывают.

А вот когда я пытаюсь создать экземпляр:
&НаКлиенте
Процедура Action()
Попытка
  Plugin =   Новый ("AddIn.TestDLL3");
Исключение
  Plugin = 0;
  Предупреждение(ОписаниеОшибки());	   
КонецПопытки;			
Если Plugin<>0 Тогда
  Plugin.TestExtEvent("Test");
КонецЕсли;
КонецПроцедуры

строка Plugin = Новый ("AddIn.TestDLL3") вылетает с ошибкой.

Выгугливание данной проблемы дает мнго вариатов решения(загрузка компоненты в макет, регистрация), но мне пока что ничего из этого не помогло. И нигде не могу найти рабочий проект.

#5 uza

    1С, VBA (EXCEL), VB (.NET + WEB)


  • 1 338 сообщений

Отправлено 22 февраля 2012 - 10:07

Чё?
Может как то так
Плагин = Новый COMОбъект("AddIn.TestDLL3");

Не все полезно - что в БД залезло

#6 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 22 февраля 2012 - 12:37

Просмотр сообщенияuza (22 февраля 2012 - 10:07) писал:

Чё?
Может как то так
Плагин = Новый COMОбъект("AddIn.TestDLL3");

Как результат, ошибка с неопределенным типом излечилась, но теперь я не могу обработать внешнее событие, которое генерируется компонентой...
Можешь поподробнее рассказать про этот аспект?

Насколько я понял, читая 1с-форумы, в 8.2 существует 2 вида внешних компонент COM и Native.
СОМ объекты не позволяют отлавливать внешние события и вообще крайне ограничены в своей функциональности.
В найденных примерах, которые якобы являются рабочими, использовались компоненты Native и как раз синтаксис вида
Plugin =   Новый ("AddIn.TestDLL3");

Код генерации события

public void TestExtEvent([MarshalAs(UnmanagedType.BStr)] string Msg)
	    {
		    System.Windows.Forms.MessageBox.Show("Creation event");
		    try
		    {
			    HOST.evt.ExternalEvent(@"External Event", @"AddIn.TestDLL3", @"Returning " + Msg);
			    System.Windows.Forms.MessageBox.Show("Event Created");
		    }
		    catch
		    {
			    throw new COMException(@"Failed to execute TestExtEvent() method");
		    }
	    }

Сообщение отредактировал alexburn: 22 февраля 2012 - 12:41


#7 uza

    1С, VBA (EXCEL), VB (.NET + WEB)


  • 1 338 сообщений

Отправлено 24 февраля 2012 - 11:35

я далек от мысли.
Яж быдлокодер на 1Сках и бэйсиках. И вообще моя родина - Индусия.
Не все полезно - что в БД залезло

#8 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 26 февраля 2012 - 14:31

В строке:
Plugin = Новый ("AddIn.TestDLL3");
должен быть не ProgID, а пространство имен. Т.е. когда вы во внешней компоненте реализовали интерфейс ILanguageExtender, то в процедуре RegisterExtensionAs этого интерфейса вы же возвращаете пространство имен. Оно и должно быть указано аргументом в функции Новый, например:
Plugin = Новый ("AddIn.MyFirstAddIn");

Now, this bell tolling softly for another, says to me: Thou must die...

#9 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 27 февраля 2012 - 07:52

Просмотр сообщенияshurikvz (26 февраля 2012 - 14:31) писал:

В строке:
Plugin = Новый ("AddIn.TestDLL3");
должен быть не ProgID, а пространство имен. Т.е. когда вы во внешней компоненте реализовали интерфейс ILanguageExtender, то в процедуре RegisterExtensionAs этого интерфейса вы же возвращаете пространство имен. Оно и должно быть указано аргументом в функции Новый, например:
Plugin = Новый ("AddIn.MyFirstAddIn");
Можно подробнее про интерфейс ILanguageExtender, если возможно с примерами реализации на С# ?
В примерах, которые приведены по ссылкам, про этот интерфейс не сказано ни слова, и я не реализовал его.
Сейчас читаю http://www.rsdn.ru/a...dotnet/cs1c.xml, но тут тоже много белых пятен: отсутствие реализации метода RegisterExtensionAs и многих других.
Буду очень благодарен за помощь!

#10 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 10:07

На rsdn самая толковая статья. Но насколько вижу - она старовата, в частности в ней я не понял реализацию некоторых интерфейсов (по крайней мере сейчас декларация некоторых функций выглядит по другому, статья была написана давно, возможно для 8.2 что-то поменялось, надо разбираться), но в любом случае - там все рабочее. И кстати метод RegisterExtensionAs - там реализован, причем на мой взгляд - достаточно толково, используя механизм Reflection.

Смотрите, все зависит от того, что у вас будет делать компонента. Если из 1С вы туда никакие данные передавать не будете, то достаточно в компоненте реализовать интерфейс IInitDone, и все.
Т.е. чтобы было понятно как работает, сделайте следующее: пишите реализацию интерфейса IInitDone (там всего 3 метода), при этом в метод Init() этого интерфейса вставляете запуск таймера (Timer), ну допустим с интервалом 5 сек (не принципиально). В событии срабатывания таймера шлете "HOST.evt.ExternalEvent(@"External Event", @"AddIn.TestDLL3", @"Returning ");".
С компонентой все (зарегистрировать ее не забудьте).
А в обработке 1С просто при открытии пишите ПодключитьВнешнююКомпоненту("AddIn.TestDLL3"); И в обработчике формы ОбработкаВнешнегоСобытия(Источник, Событие, Данные) - ловите свое событие. Все. Кстати подключать компоненту надо всего один раз. Т.е. она будет подключена пока полностью не закроете 1С:Предприятие (клиентскую часть).

Но такой простой вариант - вряд ли возможен. Вам же надо наверно все-таки как-то общаться с компонентой (что-то ей посылать). Тогда без реализации ILanguageExtender не обойтись.

Пример - есть в этой статье на rsdn, он рабочий, я проверял. Если выкрою чуть-чуть времени - постараюсь накатать статью с примером и выложить.
Now, this bell tolling softly for another, says to me: Thou must die...

#11 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 10:44

З.Ы. Кстати, если есть у кого архив "Технология создания внешних компонент" от версии 8.1 (ну или хотя-бы ранних 8.2, например 8.2.11) кто может скинуть - скиньте пожалуйста в личку (новый (актуальный), тот что сейчас на сайте, НЕ нужен, его я и сам скачать могу).
Now, this bell tolling softly for another, says to me: Thou must die...

#12 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 27 февраля 2012 - 12:40

По тех. заданию мне не нужно(пока что) отправлять данные компоненте, но мне необходимо вызывать несколько методов(ЗапускТаймера(), ОтключениеТаймера()) и получать данные из внешнего события(данные представляют из себя символьную строку).
Как я уже писал, методы интерфейса IInitDone отрабатывают у меня отлично, причём все, в том числе и Done() при закрытии клиента. Проблема возникает, когда я пытаюсь вызвать метод класса компоненты, который не входит в стандартные интерфейсы(думал даже может его туда надо вписать). Как человек имеющий некоторое представление об ООП, я пытаюсь создать экземпляр класса в 1С и тут сталкиваюсь с указанной ошибкой, так что видимо без интерфейса ILanguageExtender мне никак не обойтись.
За статью с примерами, думаю, многие будут очень благодарны, потому что реально актуальной информации в интернете я сейчас найти не могу на эту тему.

З.Ы. По поводу статьи: непонятно, где объявляются переменные, которые инициализируются в методе RegisterExtensionAs... Как я понял из статьи, для vs2003 эта проблема решается наследованием класса от ServicedComponent, или нет ? Разъясни, пожалуйста, этот момент.

#13 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 13:23

Просмотр сообщенияKimboSlice (27 февраля 2012 - 12:40) писал:

З.Ы. По поводу статьи: непонятно, где объявляются переменные, которые инициализируются в методе RegisterExtensionAs... Как я понял из статьи, для vs2003 эта проблема решается наследованием класса от ServicedComponent, или нет ? Разъясни, пожалуйста, этот момент.
Нет, наследование в данном случае не при чем (насколько помню). Переменные эти - это обычные поля класса. :


public class Component : IInitDone
{
Hashtable nameToNumber;
Hashtable numberToName;
Hashtable numberToParams;
Hashtable numberToRetVal;
и т.д.


public void RegisterExtensionAs([MarshalAs(UnmanagedType.BStr)] ref String extensionName)
{
try
{
   // initialize data members
   nameToNumber = new Hashtable();
   numberToName = new Hashtable();
   numberToParams = new Hashtable();
   numberToRetVal = new Hashtable();}
и т.д.


Проще говоря логика реализации этой процедуры в статье на rsdn следующая: она перебирает все интерфейсы класса внешней компоненты, для "системных интерфейсов" (таких как IDisposable, IManagedObject, IServicedComponentInfo и.т.д.) и обязательных для внешней компоненты 1С (таких как IInitDone, ILanguageExtender) мы ничего не делаем, а для всех остальных берем методы описанные в этих интерфейсах, и собственно получаем из них: имя метода, количество параметров и т.д. Нам это необходимо для полной реализации интерфейса ILanguageExtender (т.е. посмотри на остальные методы объявленные в этом интерфейсе - нам необходимо будет возвращать количество процедур, количество параметров в процедуре, искать процедуру по имени). Можно было и "в ручную" прописывать это в соответствующих функциях (т.е. условно например для HRESULT GetNProps(long *plProps)):


Если ИмяМетода = "а" То
  КоличествоПараметров = 1;
ИначеЕсли ИмяМетода = "б" То
  КоличествоПараметров = 3;
КонецЕсли;
Возврат КоличествоПараметров;

Но так как сделано в том примере ведь намного "красивее".

Ну и вот. Исходя из вышесказанного, помимо всего того, что описано в статье надо будет сделать одну вещь: описываешь свой интерфейс с нужными функциями (теми которые будешь вызывать), наследуешь класс компоненты дополнительно от этого интерфейса, ну и естественно в классе надо не забыть реализовать методы объявленного интерфейса. Т.е. условно получится:


public interface MyMethods //<-------
{
  public void TestExtEvent([MarshalAs(UnmanagedType.BStr)] string Msg);
}
наследуете и реализуете:

public class Component : ServicedComponent, IInitDone, ILanguageExtender, MyMethods //<-------
{

....



public void TestExtEvent([MarshalAs(UnmanagedType.BStr)] string Msg)
	    {
		    System.Windows.Forms.MessageBox.Show("Creation event");
		    try
		    {
			    HOST.evt.ExternalEvent(@"External Event", @"AddIn.TestDLL3", @"Returning " + Msg);
			    System.Windows.Forms.MessageBox.Show("Event Created");
		    }
		    catch
		    {
			    throw new COMException(@"Failed to execute TestExtEvent() method");
		    }
	    }
}


После этого метод TestExtEvent можно будет вызвать в 1С.
Now, this bell tolling softly for another, says to me: Thou must die...

#14 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 13:41

Кстати, сейчас посмотрел комментарии к той статье, там оказывается в комментариях (от 02/08/05) есть еще один пример кода по этой статье. Там вроде немного по другому сделано, но тот пример я не пробовал.
Now, this bell tolling softly for another, says to me: Thou must die...

#15 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 27 февраля 2012 - 16:00

Как вы писали выше метод RegisterExtensionAs возвращает пространство имен, которое будет использоваться 1С для создания экземпляра класса:
extensionName = componentName;
Вот эта строчка в методе, но переменной componentName значение нигде не присваивается, поясните пожалуйста, какое значение нужно присвоить componentName, я так понимаю это должна быть связка <пространство имен компоненты>.<имя класса>

#16 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 16:16

Просмотр сообщенияKimboSlice (27 февраля 2012 - 16:00) писал:

extensionName = componentName;
Вот эта строчка в методе, но переменной componentName значение нигде не присваивается, поясните пожалуйста, какое значение нужно присвоить componentName
Произвольное строковое имя. Это и будет то что вы будете писать в

Plugin = Новый ("AddIn.<вот здесь>");


Например напишите в с#:
extensionName = "MySuperPuperAddIn";
значит в 1С будете создавать как:

Plugin = Новый("AddIn.MySuperPuperAddIn");


З.Ы. Тот комментарий о котором я писал к статье (в #14 сообщении) - не совсем рабочий, проверил. Во первых нет наследования от класса ILanguageExtender (без этого не работает), и во вторых определение внешней процедуры отдельным классом - тоже не работает (ну у меня не заработало по крайней мере) (надо реализовывать через интерфейс, как я писал в #13).
Now, this bell tolling softly for another, says to me: Thou must die...

#17 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 27 февраля 2012 - 16:16

Поставил тупо
componentName = "AddIn.TestDLL3";
extensionName = componentName;
Всё прошло на ура, экземпляр класса был создан успешно, и метод
public void TestExtEvent([MarshalAs(UnmanagedType.BStr)] string Msg)
	    {
		    System.Windows.Forms.MessageBox.Show("Creation event");
		    try
		    {
			    HOST.evt.ExternalEvent(@"External Event", @"AddIn.TestDLL3", @"Returning " + Msg);
			    System.Windows.Forms.MessageBox.Show("Event Created");
		    }
		    catch
		    {
			    throw new COMException(@"Failed to execute TestExtEvent() method");
		    }
	    }
успешно был вызван и отработал без ошибок, но внешнее событие 1С-ка не поймала... С чем это может быть связано ?
Нужно ли чтобы класс наследовал интерфейс IAsyncEvent ?

Сообщение отредактировал KimboSlice: 27 февраля 2012 - 16:18


#18 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 27 февраля 2012 - 16:21

Просмотр сообщенияKimboSlice (27 февраля 2012 - 16:16) писал:

Нужно ли чтобы класс наследовал интерфейс IAsyncEvent ?
Нет.

Просмотр сообщенияKimboSlice (27 февраля 2012 - 16:16) писал:

успешно был вызван и отработал без ошибок, но внешнее событие 1С-ка не поймала... С чем это может быть связано ?
А в 1С на форме процедура ОбработкаВнешнегоСобытия сопоставлена с этой формой? (как заводили эту процедуру? Просто в модуле формы написали?) Надо в визуальном редакторе свойства формы вызвать, и там внизу где события сопоставить соответствующее событие с процедурой в модуле формы.
Now, this bell tolling softly for another, says to me: Thou must die...

#19 KimboSlice

    Прохожий


  • 9 сообщений

Отправлено 28 февраля 2012 - 12:07

Shurikvz, огромное спасибо за помощь, благодаря Вашим консультациям проблемы подключения внешней компоненты были успешно решены. Ещё раз спасибо!

Теперь столкнулся с другой проблемой: в функционал разрабатываемой компоненты входит прослушивание порта и в генерация события, в случае поступления определенной информации. Изначально этот функционал был реализован в .net приложении, и там всё это работало методом создания стороннего потока, в котором прослушивался порт, а в основной поток данные передавались с помощью соответствующего делегата. Так вот при создании потока в компоненте, 1С-ка виснет. Может кто знает с чем это связано ? И как организуются такие задачи в целом ?

#20 shurikvz

    Ветеран


  • 5 231 сообщений

Отправлено 28 февраля 2012 - 13:29

Просмотр сообщенияKimboSlice (28 февраля 2012 - 12:07) писал:

Shurikvz, огромное спасибо за помощь, благодаря Вашим консультациям проблемы подключения внешней компоненты были успешно решены. Ещё раз спасибо!
Не за что.


Просмотр сообщенияKimboSlice (28 февраля 2012 - 12:07) писал:

Теперь столкнулся с другой проблемой: в функционал разрабатываемой компоненты входит прослушивание порта и в генерация события, в случае поступления определенной информации. Изначально этот функционал был реализован в .net приложении, и там всё это работало методом создания стороннего потока, в котором прослушивался порт, а в основной поток данные передавались с помощью соответствующего делегата. Так вот при создании потока в компоненте, 1С-ка виснет. Может кто знает с чем это связано ? И как организуются такие задачи в целом ?
А вот здесь боюсь моих знаний недостаточно. Как вариант - может быть как-то связано с моделью STA - MTA?
Now, this bell tolling softly for another, says to me: Thou must die...





Количество пользователей, читающих эту тему: 1

0 пользователей, 1 гостей, 0 анонимных