Имя: Пароль:
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) спасибо.
Сейчас буду учиться возвращать результат выполнения другой функции, тут уже по срр нужно читать.
Здесь можно обсудить любую тему при этом оставаясь на форуме для 1Сников, который нужен для работы. Ymryn