Имя: Пароль:
1C
1С v8
v8: Особенности dll, написанной на С++
,
0 Menjoy
 
03.12.11
21:07
Добрый день.

Понадобилось написать компоненту с нуля на С++ (visual studio 2008). Был реализован основной интерфейс IInitDone, но dll не загружается 1Ской:

Ошибка при вызове метода контекста (ЗагрузитьВнешнююКомпоненту): Ошибка при загрузке внешней компоненты

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

За основу брал статью http://oksla.narod.ru/vk.htm#_Toc502332887 и уже готовую компоненту.
1 SnarkHunter
 
03.12.11
21:11
Видимо включен UAC...
2 Menjoy
 
03.12.11
21:11
(1) можно чуть подробнее? Что это?
3 Menjoy
 
03.12.11
21:14
Уже прочитал в вики, все делается на win хр sp3
4 bezgudroman
 
03.12.11
21:34
А точно нужна именно внешняя компонента?
5 bezgudroman
 
03.12.11
21:39
Может вот так попробовать: http://unnstudioreport.googlecode.com/files/examplVK.zip
6 Menjoy
 
03.12.11
21:56
(4) да, нужна именно внешняя.
Ваша компонента тоже не подгружается 1С и в исходниках нужных интерфейсов и функций я не вижу. У вас работает?
7 bezgudroman
 
03.12.11
22:05
(6) >> У вас работает?

Конечно.
Там же написано: "Сразу оговорюсь, в итоге получится обыкновенный inprocess server, а не "внешняя компонента" в понимании 1С. В этом примере не используется "Технология создания внешних компонент" от 1С (где-то она у меня на старом винте заблудилась - найти не могу), и поэтому описанная ниже технология подходит для задач типа "вы спрашиваете - мы отвечаем"."
8 bezgudroman
 
03.12.11
22:05
Что делает твоя ВК?
9 bezgudroman
 
03.12.11
22:13
10 Menjoy
 
03.12.11
22:24
(8) компонента получает данные от 1С (сервер, порт, логин, пароль) и подключается к серверу, который в свою очередь постоянно передает данные компоненте, эти данные обрабатываются и уже в нужном виде попадают в 1С.

Кхм, попробовал твою компоненту подключить как ЗагрузитьВнешнююКомпоненту("E:\Example.dll");
предварительно зарегистрировав, та же ошибка.
11 Menjoy
 
03.12.11
22:27
Есть исходники уже рабочей компоненты, но без поддержки MFC.
Поэтому решил создать проект с нуля, взял из предыдущего все обязательные интерфейсы и методы, все равно не подключает.

p.s. а каким образом подключается эта компонента у тебя?
12 bezgudroman
 
03.12.11
22:45
В архиве есть файло: example.htm
Там в конце написано:

Процедура Сформировать()
 В=СоздатьОбъект("example.Random");
 // Random возвращает значение от 0 до 1000 в данном случае
 Всп=В.GetRandomValue(1000);
 Сообщить(Всп);
КонецПроцедуры
13 Rie
 
03.12.11
22:46
(11) v8 - имеется в виду 8.1 или 8.2?
14 Rie
 
03.12.11
22:48
+(13) И какую компоненту рисуешь: под NativeAPI или под COM?
15 Menjoy
 
03.12.11
22:59
v8 в моем случае 8.2
(14) СОМ
16 Rie
 
03.12.11
23:01
(15) Покажи реализацию IInitDone.
17 Rie
 
03.12.11
23:06
+(16) И ещё деталь - права на запись в реестр в HKEY_CLASSES_ROOT у пользователя есть?
18 Rie
 
03.12.11
23:12
+(17) Ну и ещё вопрос - а IDispatch реализован (хотя бы заглушками)?
19 orefkov
 
04.12.11
00:14
Никаких особенностей в длл на С++ нет.
Реализуйте все правильно, и все будет работать.
В ТВК пример на С++ есть.
И что все у народа какие-то сложности с ВК?
Делов то там - ПРАВИЛЬНО реализовать inproc server да пару интерфейсов.
Ну и в реестре правильно описать.
20 Menjoy
 
04.12.11
01:38
(16)
права на запись в реестр есть, все записывается, искал в реестре по прогид.

Реализация интерфейса IInitDone

[
       object,
       uuid(8FE5C6B3-2758-4557-B8AB-9E8C2D764E4B),
       helpstring("IInitDone Interface"),
       pointer_default(unique)
   ]
   interface IInitDone : IUnknown //инициализация и выгрузка 1С
   {
       [helpstring("method Init")] HRESULT Init([in] IDispatch *pConnection);
       [helpstring("method Done")] HRESULT Done();
       [helpstring("method GetInfo")] HRESULT GetInfo([in,out] SAFEARRAY (VARIANT) *pInfo);
   };


Методы упростил и сделал так, чтобы они только возвращали S_OK.
21 Menjoy
 
04.12.11
01:45
(19) в реестре все прописывается правильно - CLSID и ProgID добавляются.
Ошибка именно при вызове метода ЗагрузитьВнешнююКомпоненту();
У меня у самого есть исходник внешней компоненты (которая у меня корректно работает), но без поддержки MFC, да и нужно научится просто создавать заготовку dll для 1С.

А примера на С++ из ТВК не видел, выслать можете?
22 orefkov
 
04.12.11
01:57
(21)
Ну и скажи, каков ProgID твоей ВК ?
23 orefkov
 
04.12.11
02:11
+(22)
Надеюсь, удовлетворяет вот этому:
"При загрузке внешней компоненты функцией ЗагрузитьВнешнююКомпоненту 1С:Предприятие определяет ProgID OLE-объекта компоненты следующим образом:
ProgID имеет вид <Vendor>.<Component>;
в качестве первой части (<Vendor>) используется строка "AddIn";
в качестве второй части (<Component>) используется строка с ID 100 из таблицы строк компоненты. Cтрока может иметь вид "Name1|Name2|...|NameN", и в этом случае будут созданы все объекты с ProgID вида "AddIn.NameX". Если такая строка отсутствует, то используется имя файла внешней компоненты без расширения."
24 Rie
 
04.12.11
07:57
(20) Это не _реализация_, это _описание_ интерфейса.
Что касается "упростил" - то метод GetInfo должен не только вернуть S_OK, но и правильное значение версии установить.
25 Menjoy
 
04.12.11
12:52
(23)
ProgID следующий - com_1c.TSoc.1 - такой прописался в реестре.
А <vendor> обязательно должен быть "AddIn".
Если так, то возможно именно в этом проблема, проверю.

(24)
GetInfo() так же пробовал использовать из рабочего исходника.
26 Rie
 
04.12.11
13:10
(25) Ещё обрати внимание на ресурс 100.
27 orefkov
 
04.12.11
13:50
(25)
Смени прогид на "Addin.ИмяТвоегоФайлаБезРасширения"
28 Menjoy
 
04.12.11
17:21
Теперь, судя по всему ЗагрузитьВнешнююКомпоненту(); срабатывает, ошибки нет.
Но не создается объект - ВК = Новый ("AddIn.AddInSoc");
Ошибка - Тип не определен (AddIn.SocDll).
ProgID (независимый от версии) в реестре прописан такой же, пробовал и с AddIn.SocDll.1
29 Rie
 
04.12.11
17:23
(28) Что в ресурсе 100? Какой ProgID в реестре?
31 Menjoy
 
04.12.11
17:28
Под ID 100 лежит AddInSoc
В реестре AddIn.AddInSoc
33 Rie
 
04.12.11
17:33
(28) Новый COMОбъект("AddIn.AddInSoc")?
34 Menjoy
 
04.12.11
17:33
В сообщение (28) кое что перепутал, там везде AddIn.AddInSoc вместо SocDll
36 Menjoy
 
04.12.11
17:35
(33) Ошибка при вызове конструктора (COMОбъект): Интерфейс не поддерживается: Интерфейс не поддерживается

При таком подходе, получается что интерфейс какой-то не реализован просто. В этой длл есть только IInitDone и все.
Кстати, исходник, который у меня есть, его длл подключается через Новый (""); т.е. без COMОбъект.
37 Rie
 
04.12.11
17:36
(36) Заглушка IDispatch есть? И как именно сейчас выглядит GetInfo?
39 Menjoy
 
04.12.11
17:41
Вот GetInfo()

STDMETHODIMP CAddInSoc::GetInfo(SAFEARRAY **pInfo)
{
   // Component should put supported component technology version
   // in VARIANT at index 0    
   long lInd = 0;
   VARIANT varVersion;
   V_VT(&varVersion) = VT_I4;
   V_I4(&varVersion) = 2000;
   SafeArrayPutElement(*pInfo,&lInd,&varVersion);
   
   return S_OK;
}


А что за заглушка IDispatch?
40 Rie
 
04.12.11
17:44
(39) Реализовать IDispatch - пусть его методы просто возвращают E_NOTIMPL.
41 Menjoy
 
04.12.11
17:54
(40) Он же реализован по умолчанию.

#if defined(__cplusplus) && !defined(CINTERFACE)
   
   MIDL_INTERFACE("00020400-0000-0000-C000-000000000046")
   IDispatch : public IUnknown
   {
   public:
       virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
           /* [out] */ __RPC__out UINT *pctinfo) = 0;
       
       virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(
           /* [in] */ UINT iTInfo,
           /* [in] */ LCID lcid,
           /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = 0;
       
       virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(
           /* [in] */ __RPC__in REFIID riid,
           /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
           /* [range][in] */ UINT cNames,
           /* [in] */ LCID lcid,
           /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = 0;
       
       virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
           /* [in] */ DISPID dispIdMember,
           /* [in] */ REFIID riid,
           /* [in] */ LCID lcid,
           /* [in] */ WORD wFlags,
           /* [out][in] */ DISPPARAMS *pDispParams,
           /* [out] */ VARIANT *pVarResult,
           /* [out] */ EXCEPINFO *pExcepInfo,
           /* [out] */ UINT *puArgErr) = 0;
       
   };
42 Rie
 
04.12.11
17:58
(41) Твой код я не вижу, а телетяпией не страдаю :-)

Какой интерфейс не реализован - вероятно, ILanguageExtender.
43 Menjoy
 
04.12.11
18:02
Да-да, именно, только что сам к этому пришел - ILanguageExtender.
Подгрузил другую компоненту как СОМ и увидел, что она вернула два значения:
1) Включено - 0;
2) ТаймерПрисутствует - 1;

Отсюда и понял ))
Спасибо, пожалуй займусь в рабочий день.
44 orefkov
 
04.12.11
18:13
ILanguageExtender надо
45 Menjoy
 
04.12.11
19:21
(42) (44)
а если будет реализован ILanguageExtender сработает ли просто ВК = Новый ("ProgID")?
46 Rie
 
04.12.11
19:44
(45) Новый COMОбъект("ProgID")
47 Rie
 
04.12.11
19:47
+(46) Хотя нет. Тут я соврал насчёт COMОбъект.
48 Menjoy
 
04.12.11
20:01
(47) немного не понял :)
Есть исходники компоненты, на которую я полагался в своей разработке, так вот она вызывается просто как Новый("ProgID") и получается ком-объект, проверял в отладчике.
49 Rie
 
04.12.11
20:07
(48) Я и говорю - соврал я насчёт того, что надо писать именно Новый COMОбъект.
50 Rie
 
04.12.11
20:09
+(49) Ты б поставил в методах IInitDone и ILanguageExtender вызовы MessageBox - и сразу стало бы ясно, на каком этапе у тебя проблемы.
51 Menjoy
 
04.12.11
20:10
(50) Займусь дальше только в понедельник ;) Если что, то буду продолжать тему )) Не нашел подобных на просторах интернета, потому и начал свою.

Спасибо за совет на счет MessageBox, попробую.
52 Rie
 
04.12.11
20:11
(51) Если действовать "строго по инструкции", соблюдая все требования - проблем не будет. Проверено :-)
53 Menjoy
 
08.12.11
13:02
По-тиху продвигаюсь вперед и возник такой вопрос.
Как правильно создается внешнее событие?

Сделал спец. функцию и повесил ее на кнопку в 1С, чтобы тренироваться.
Функция в срр имеет следующий вид:



VOID CALLBACK Connection(CString value1, CString value2)
{
   BSTR who, what, data;
   CString whois;
   whois = L"DLL";
   who = whois.AllocSysString();
   what = value1.AllocSysString();
   data = value2.AllocSysString();
   pAsyncEvent->ExternalEvent(who, what, data);
}


Но внешнее событие не ловится формой :(
В модуле формы 1С прописано:

Процедура ВнешнееСобытие(Источник, Событие, Данные)
     Если Событие = "1" Тогда
         Сообщить("Внешнее событие получено!");
     КонецЕсли;
КонецПроцедуры


При отладке value1 задаю 1.
54 Menjoy
 
08.12.11
13:05
При отладке еще выловил такую ошибку:

Unhandled exception at 0x06375e01 (AddInCOM.dll) in 1cv8.exe: 0xC0000005: Access violation reading location 0x00000000.
55 Rebelx
 
08.12.11
13:07
(10) а может ну ее эту компоненту? что нельзя сделать на 1С? а еще можно использовать JS для бинарных данных
56 Menjoy
 
08.12.11
13:12
(55) нужно, возможностей куча, к тому же с++ быстрее обрабатывает информацию ;)
Меня больше интересует ответ на вопрос, нежели споры о полезности компонент)
57 Serginio1
 
08.12.11
13:15
Возьми отсюда http://1c.proclub.ru/modules/mydownloads/personal.php?cid=115&lid=2019
Исходник ВК которая загружает Объект Автоматизации поддерживающий ITypeInfo и выполняет все его свойства и методы через IlanguageExtender.

Где там есть еще и исходники аналогичной приблуды на C++
58 Menjoy
 
08.12.11
13:20
(57) то, что вы скинули - на delphi
И все же хочется найти ошибку у себя.
59 Serginio1
 
08.12.11
13:20
60 jsmith82
 
08.12.11
13:25
я пишу на остром си - всё без проблем
61 Rie
 
08.12.11
13:38
(54) Эта ошибка произошла в коде на С++! Там её и надо искать, сам механизм внешних компонент тут ни при чём.
Поставь отладочный вывод. Определи, в какой строке происходит исключение. Или перехвати его try ... catch - и выдай результат. Где вызывается Connection - никому, кроме тебя не известно. Что за значения у каких переменных - тоже.
62 Menjoy
 
08.12.11
13:56
Connection вызываю из 1С, он доступен через внешнюю компоненту.
Ошибку выдает именно в этой строке:

pAsyncEvent->ExternalEvent(who, what, data);

Думаю, что может быть связано с строкой в срр файле:

static IAsyncEvent *pAsyncEvent = NULL;
63 Rie
 
08.12.11
13:59
(62) Нет, ну трах-тибидох! Естественно с этим связано!!!
Обращаться к методу объекта по указателю NULL - это круто!

pAsyncEvent инициализируй в IInitDone::Init.
64 Menjoy
 
08.12.11
13:59
Ошибка при вызове любого из методов pAsyncEvent.
65 Menjoy
 
08.12.11
13:59
(63) извиняюсь за такие вопросы, но я пару дней как вижу С++ ;)
66 Rie
 
08.12.11
13:59
(64) Меня это не удивляет. Когда вызываются методы объекта по указателю NULL - так всегда бывает.
67 Rie
 
08.12.11
14:02
(65) Тогда зря ты сел за написание на нём ВК. Лучше освой язык - а потом уже пиши.
68 Menjoy
 
08.12.11
14:10
(63)
а каким образом правильно инициализировать pAsyncEvent в IInitDone::Init?
в данном контексте просто не ясно, что значит инициализовать
69 Rie
 
08.12.11
14:14
(68) При помощи GetInterface получить IAsyncEvent из того IDispatch, который тебе в Init передан.
70 Menjoy
 
08.12.11
14:16
Упс, (68) было очень поспешным.
Действительно, нужно было просто добавить строку pAsyncEvent = m_iAsyncEvent; в объявлении метода Connection.
Т.к. в IInitDone::Init инициализирован:

m_iConnect->QueryInterface(IID_IAsyncEvent,(void **)&m_iAsyncEvent);
71 Menjoy
 
08.12.11
14:16
(69) согласен конечно с замечанием про подучить язык, но так обучаться интереснее. К тому же мне нужно изучить всего лишь некоторые аспекты языка :)
Тем не менее, спасибо за уже оказанную помощь и что тему поглядываешь ))
72 Rie
 
08.12.11
14:29
(70) Если у тебя уже есть член с именем m_iConnect (а судя по префиксу m_ - это именно член) - зачем ещё static-переменную заводить?
73 Menjoy
 
08.12.11
14:57
(72) вот уж не знаю, она объявлена была в исходниках, по-немногу разгребаюсь с ними :)
74 Menjoy
 
13.12.11
13:03
Пытаюсь сделал возвращаемое функцией значение, причем не булево, а допустим результат сложения двух чисел.
Может кто пример описания метода в HRESULT CallAsFunc(long lMethodNum, VARIANT *pRetValue, SAFEARRAY**pVars) показать?
75 orefkov
 
13.12.11
13:09
pRetValue->vt = VT_I4;
pRetValue->intVal = 10;
76 Menjoy
 
13.12.11
13:28
(75) спасибо.
Сейчас буду учиться возвращать результат выполнения другой функции, тут уже по срр нужно читать.
Закон Брукера: Даже маленькая практика стоит большой теории.