Текущий архив: 2006.05.07;
Скачать: CL | DM;
ВнизКак правильно зарегить плагин к 1С ? Найти похожие ветки
← →
ANB © (2006-04-12 17:45) [0]На основе заготовки MASTER и SAMPLE ничего не работало.
Взял SAMPLE2 (который ролики крутит), переделал для себя - работает.
Запустил на другом компе - не находит CLSID.
Все понятно - не зарегился COM сервер.
Снес у себя ветки в реестре - воспризвел ошибку.
Начал ломать кусочками.
Исправил GUID класса TAddInVideo - работает.
Снес проигрыватель и все, что его касается - работает.
Изменяю строку :
initialization
TComObjectFactory.Create(ComServer, TAddInOfficeReport, Class_AddInOfficeReport,
"AddInVideo", "", ciMultiInstance, tmApartment);
на на
TComObjectFactory.Create(ComServer, TAddInOfficeReport, Class_AddInOfficeReport,
"AddInMyPluggin", "", ciMultiInstance, tmApartment);
еще работает, но если после этого сменить GUID - вопит, что нет интерфейса IInitDone.
Меняю GUID, ставлю на место AddInVideo - снова все работает.
Вывод - где то ломаю регистрацию. Еще зачем то тащится форма AddInDoc, которая мне совсем не нужна (со своей регистрацией и TLB).
Кто знает - какие минимальные телодвижения нужно сделать, чтобы плагин в 1С нормально регистрился и загружался ?
← →
sniknik © (2006-04-12 17:53) [1]а он у тебя по правилам 1С (для 7ки там муторно както) или простой COM обьект?
если простой то regsvr32 AddInVideo.dll (ну или что у тебя за dll там получилась)
если по правилам, то ничего не надо, 1С сама умеет такие регистрить (только не помню функцию ;)
← →
sniknik © (2006-04-12 17:56) [2]> только не помню функцию
может эти LoadAddIn и CreateObject (похожи ;)
← →
ANB © (2006-04-12 17:58) [3]
> sniknik © (12.04.06 17:53) [1]
По правилам 1С. Действительно, очень мутно, но как сделать просто и при этом получать от 1С ссылку на ее экземпляр (в Init приезжает) ?
1С не регистрит, она заружает плагин :
ЗагрузитьВнешнююКомпоненту(Файл_Компоненты)
А компонента при этом должна сама регистряться.
← →
ANB © (2006-04-12 17:59) [4]Если плохо регистряется - то потом ничего не работает.
← →
sniknik © (2006-04-12 18:34) [5]> при этом получать от 1С ссылку на ее экземпляр
просто не знаю, только по 1C-овски "парится"
> 1С не регистрит, она заружает плагин :
а по моему то же самое и делает, регистрит... (убрать все, "загрузить" и посмотреть в реестре...)
> ЗагрузитьВнешнююКомпоненту
LoadAddIn это она и есть, "по иностранному" ;)
> Если плохо регистряется - то потом ничего не работает.
значит чегото не так... скачай нормальный, сторонний пример, 1С-совские (с книжкой "технология создания внешних компонент") у меня ни один нормально не работал... во всех глюки какиенибудь да были. (на hare.ru (вроде), раньше неплохие лежали... сейчас сайт не открывается... почемуто, толи название подзабыл толи сайту каюк ;)
← →
ANB © (2006-04-13 09:07) [6]
> скачай нормальный, сторонний пример, 1С-совские
Сейчас поищу в яндексе. И у наших поспрошаю.
У меня из родных 1С-ских примеров завелся только тот, что с роликами. Но стоит его чуток изменить - и он ломается.
Реестр я смотрел - регится класс по имени и гуиду, трешь ветки - все ломается.
← →
tesseract © (2006-04-13 10:39) [7]anb - мой
класс нужен. Там кое-что даже документировано :-)
> если по правилам, то ничего не надо, 1С сама умеет такие
> регистрить (только не помню функцию ;)
Из моей возможно будущей статьи :-)
Описание процессов, происходящих при загрузке компоненты
Загрузка компоненты :
ЗагрузитьВнешнююКомпонету - загружает и инициализирует компоненту из библиотеки
1 - Регистрируется HKCR "имя сервера.Имя класса". Имя сервера указанное в ComServer.SetServerName.
и предположительно должно быть AddIn. Имя класса(ов) - указанное в ID 100 ресурсов файла.
2 - Попытка создания Com-объекта;
-> Если ID 100 отсутсвует/ неправилен, вы получите "отсутсвует CLSID".
Регстрация компонеты закончена, начиная с этого шага можно использовать ПодключитьВнешнююКомпоненту
{создаётся оъект интерфейса iInitDone}
3 - Вызывается init(pConn:IDispatch).
Происходит инициализация объекта, инициализация используемых интерфейсов.
-> Вызовом RegisterProfileAs(const bstrProfileName: WideString) происходит регистрация ключа реестра, где храняться наши настройки.
-> По умолчанию они -> Имя нашего модуля + "_profile"
-> Происходит вызов метода moduleinit .
4 - Вызывается GetInfo(var pInfo: PSafeArray) - выяcняется версия OLE
{создаётся объект интефейса IlanguageExtender}
5 - Вызывается RegisterExtensionAs(var bstrExtensionName: WideString)
Компонета регистрируется как "Имя класса" в 1с.
6 - Вызывается создание Com-объекта уже как "addin.Имя класса", проверяется возможность создания зарегистрированного объекта
-> Если RegisterExtensionAs указан не тот объект, вы получите "отсутсвует CLSID".
{создаётся объект интерфеса ISpecifyPropertyPage }
7 - вызывается GetPages(out Pages: TCAGUID) (при использовании ISpecifyPropertyPage)
выясняется наличие страниц свойств.
-> При регистрации компоненты, как торгового оборудования, вы увидите сообщение о том, что страница свойств объекта доступна
-> В меню "сервис" -> "параметры".
ПодключитьВнешнююКомпоненту("Addin.имя Класса")
загружает и инициализирует компоненту с CLSID переданным, как параметр. Есть предположение, что ЗагрузитьВнешнююКомпоненту вызывает эту
функцию начиная с шага 3.
СоздатьОбъект("Addin.имя Класса")
практически равносильно ЗагрузитьВнешнююКомпоненту.
Но выполняются только шаги 2,3,5,7.
Конец инициализации
При выгрузке компоненты вызывается метод DONE.
Вызывается он при закрытии обработке в которой использован СоздатьОбъект, и при закрытии 1с (ИМХО только если использован ILanguageExtender)
-> вызывается ModuleDone.
Вызов Функции/процедуры :
Данная последовательность вызывается каждый раз при вызове процедуры!
-> Из чего следует наличие почти полноценного "RTTI".
1 - Вызов FindMethod(const bstrMethodName: WideString; var plMethodNum: Integer) с именем метода.
Метод может быть, как русским так и английским.
2 - Вызов GetNParams(lMethodNum: Integer; var plParams: Integer)
Выяснение кол-ва аргументов процедуры/функции.
3 - Вызов HasRetVal(lMethodNum: Integer; var pboolRetValue: Integer)
Выяснение - процедура или функция это у нас.
4 - Вызов GetParamDefValue(lMethodNum, lParamNum: Integer; var pvarParamDefValue: OleVariant);
Функция вызывается, если в функцию передали меньше значений, чем вернула GetNParams.
Функция вызывается по разу за каждый неуказанный параметр.
5. CallAsFunc или CallAsProc соотвественно.
Получение/запись свойства объекта
1. Вызов FindProp(const bstrPropName: WideString; var plPropNum: Integer) с именем свойства.
Метод может быть, как русским так и английским.
2. Если свойство присваивается, то вызывается IsPropWritable(lPropNum: Integer; var pboolPropWrite: Integer)
-> Если свойство считывается, то НИЧЕГО не вызвается, прошу обратить внимание!!!!!
-> Все свойства по умолчанию являются "Для чтения".
-> Если вы хотите иметь его только для чтения в TcustomAddin имеется заглушка на сей счёт.
3. Вызывается GetPropVal(lPropNum: Integer; var pvarPropVal: OleVariant)
или SetPropVal(lPropNum: Integer; var varPropVal: OleVariant)
Работа с асинхронным событием
1. Компонета вызовом ExternalEvent помещает событие в очередь ожидания
2. Если в текущей (активной!) обработке/документе/отчёте модуле присутсвует функция ОбработкаВнешнегоСобытия
то ей передаётся управление.
3. Выполянется интерпритация события
← →
ANB © (2006-04-14 11:08) [8]
> tesseract © (13.04.06 10:39) [7]
Текс. Собрал всю доку, плюс эту рекомендацию.
Взял пустой шаблон MASTER и начал делать.
Что сделал :
1. Сохранил проект под новым именем
2. Это же имя объявил константой.
3. Заменил GUID в unit PropPage
4. Заменил GUID в unit AddInObj;
5. Запихнул константу с именем в :
- раздел initialization
- метод RegisterExtensionAs
- метод Init в регистрацию профиля (ничего про него не понял)
6. В ресурс проекта добавил строку с ID = 100 и именем проекта.
Исходники запостю ниже.
Что получил :
В 1С вызываю :
Файл_Компоненты = "OfficeReport.dll";
Сообщить (Файл_Компоненты);
Если ЗагрузитьВнешнююКомпоненту(Файл_Компоненты) = 0 Тогда
Сообщить("ошибка загрузки");
Возврат;
КонецЕсли;
По всем методам плагина повтыкал отладочные сообщения (отладчик потом подрублю, когда надо будет логику отлаживать).
Вижу :
1. Вызван раздел инициализации (прошла регистрация, в нем менял имя вTComObjectFactory.Create(ComServer,TAddInObject,CLSID_AddInObject,
)
AddIn_Name, "V7 AddIn 2.0", ciMultiInstance);
2. Вызван Init (в нем менял только имя профиля)
3. Вызван GetInfo (только вставил ShowMessage)
4. Вызван Done
1С вываливает ошибку :
Ошибка при инициализации объекта из компоненты C:\Program Files\1Cv77\1SBDemo\OfficeReport.dll
Ничего не понимаю - что я делаю не так ?
← →
ANB © (2006-04-14 11:12) [9]Постю куски исходника :
Объявление констант :
// Здесь нужно описать строки, которые станут рассширением языка в 1С
resourcestring
strWordReport = "Word_Report,Отчет_В_Ворд";
const
// Этот GUID нужно обязательно перегенерить
CLSID_AddInObject : TGUID = "{E3FB6F4A-11AD-4B3C-954F-9EAA72B9D4D6}";
// В эту константу нужно записать имя плагина
// Проще, если оно будет совпадать с именем проекта
AddIn_Name = "OfficeReport";
// Это же имя нужно еще записать в ресурс проекта в строку с ID = 100.
type
// Номера свойств
TProperties = ( LastProp );
// Номера методов
TMethods = ( methWordReport, LastMethod );
← →
ANB © (2006-04-14 11:13) [10]Метод Init :
function TAddInObject.Init(pConnection: IDispatch): HResult; stdcall;
var iRes : Integer;
begin
ShowMessage("Init");
// pConnect := pConnection;
pErrorLog := nil;
pConnection.QueryInterface(IID_IErrorLog,pErrorLog);
pEvent := nil;
pConnection.QueryInterface(IID_IAsyncEvent,pEvent);
pProfile := nil;
iRes := pConnection.QueryInterface(IID_IPropertyProfile,pProfile);
if (iRes = S_OK) then
begin
pProfile.RegisterProfileAs(AddIn_Name + "_Profile");
if (LoadProperties() <> True) then
begin
Init := E_FAIL;
Exit;
end;
end;
pStatusLine := nil;
pConnection.QueryInterface(IID_IStatusLine,pStatusLine);
Init := S_OK;
end;
← →
ANB © (2006-04-14 11:13) [11]Метод GetInfo :
function TAddInObject.GetInfo(var pInfo: PSafeArray{(OleVariant)}): HResult; stdcall;
var varInfo : OleVariant;
begin
ShowMessage("GetInfo");
varInfo := "2000";
PutNParam(pInfo, 0, varInfo);
GetInfo := S_OK;
end;
← →
ANB © (2006-04-14 11:14) [12]Раздел инициализации :
initialization
ShowMessage("Регистрация");
ComServer.SetServerName("AddIn");
TComObjectFactory.Create(ComServer,TAddInObject,CLSID_AddInObject,
AddIn_Name, "V7 AddIn 2.0", ciMultiInstance);
end.
← →
ANB © (2006-04-14 11:29) [13]Такс. Попробовал запустить DLL на другом компе - срабатывает только раздел инициализации, потом ругнулась, что не найден интерфейс IInitDone. Убил ветку в реестре - начала ругаться, что не найден CLSID.
← →
ANB © (2006-04-14 11:30) [14]Убил ветку AddIn.OfficeReport у себя на компе - тоже пошла ошибка "отсутствует CLSID"
← →
ANB © (2006-04-14 12:21) [15]Убил дополнительно в реестре ветку с GUID плагина - вернуалсь первоначальная ситуация. Ща закиплю.
← →
ANB © (2006-04-14 13:53) [16]Укоротил имя плагина - не помогло.
← →
ANB © (2006-04-17 09:49) [17]
> tesseract © (13.04.06 10:39) [7]
Помоги !!!
← →
ANB © (2006-04-17 12:05) [18]Внимательно читал исходник и трассировал выполнение - выяснил, что 1С сама не выполняет регистрацию. DLL регит сама себя при выполнении раздела инициализации.
← →
Сергей М. © (2006-04-17 13:34) [19]
> читал исходник
Неужели исходник 1С ?!
← →
tesseract © (2006-04-17 15:39) [20]
> Помоги !!!
А в мэйл слабо????
iRes := pConnection.QueryInterface(IID_IPropertyProfile,pProfile);
if (iRes = S_OK) then
begin
pProfile.RegisterProfileAs(AddIn_Name + "_Profile");
if (LoadProperties() <> True) then
begin
Init := E_FAIL;
Exit;
end;
end;
Выруби это или хотя-бы E_FAIL не возвращай.
> Убил ветку AddIn.OfficeReport у себя на компе - тоже пошла
> ошибка "отсутствует CLSID"
Такое бывает при отсутсвии SetServerName. или id100.
Рабочий код инициализации:
ComServer.SetServerName("AddIn");
TComObjectFactory.Create(ComServer,tInterBase, Class_InterbaseAddin,
"InterBase", "interbase addin class", ciSingleInstance, tmApartment);
← →
ANB © (2006-04-17 16:37) [21]
> Сергей М. © (17.04.06 13:34) [19]
Не, примеров.
> tesseract © (17.04.06 15:39) [20]
Я вообще коментил все получение интерфейсов.
Короче, кое как получилось.
1. Взял рабочий плагин (был на работе)
2. Аккуратно вытер все чужое и добавил свое
3. Переименовал проект в имя плагина
4. Изменил имена плагина в 2-х местах - инициализации и RegisterExtensionAs
5. Изменил имя профиля
6. Сменил GUID
7. Скопировал под новым именем ресурс проекта и строку с ID 100 исправил на имя плагина.
8. Перекомпилял все
9. Почистил реестр (пришлось с утра прожку написать для почистки)
И все заработало. Сравнил работающий проект с примером 1С - разницы не нашел.
Единственное - таки нарвался на граблю, при которой 1С остается в списке задач.
← →
ANB © (2006-04-17 16:37) [22]
> tesseract © (17.04.06 15:39) [20]
Выслать рабочий код для анализа (для статьи) ?
← →
tesseract © (2006-04-17 17:10) [23]
> Выслать рабочий код для анализа (для статьи) ?
У меня примеров ~ 10-20. рабочих :-)
> Единственное - таки нарвался на граблю, при которой 1С остается
> в списке задач.
Не правильно задан DONE. Наверно не освободил какие-нибудь ресурсы.
← →
ANB © (2006-04-17 17:40) [24]
> Не правильно задан DONE. Наверно не освободил какие-нибудь
> ресурсы.
Не. Это я в своем обработчике намутил, дергая AppDispatch. Надо разбираться и чинить.
Отключаешь - все работает как надо.
← →
ANB © (2006-04-17 17:41) [25]
> tesseract © (17.04.06 17:10) [23]
Кстати, как правильно достать и освободить AppDispatch ?
← →
tesseract © (2006-04-18 09:55) [26]
> Кстати, как правильно достать и освободить AppDispatch ?
В смысле как избавится от интерфейса который тебе 1c вернул? в null его :-)
← →
ANB © (2006-04-18 12:25) [27]
> tesseract © (18.04.06 09:55) [26]
Просто нулл не помогал.
Короче - вот так работает :
Application_1C : Variant;
....
try
Application_1C := Null;
Application_1C := Variant(pConnect).AppDispatch;
// pConnect я заранее сохранил в Init как IDispatch
IDispatch(Application_1C)._AddRef;
....
finally
Application_1C := Null;
end;
Методы Init и Done такие :
function TAddInObject.Init(pConnection : IDispatch): HResult; stdcall;
var iRes : Integer;
begin
pConnect := pConnection;
pErrorLog := nil;
pConnect.QueryInterface(IID_IErrorLog,pErrorLog);
pEvent := nil;
pConnect.QueryInterface(IID_IAsyncEvent,pEvent);
pProfile := nil;
iRes := pConnect.QueryInterface(IID_IPropertyProfile,pProfile);
if (iRes = S_OK) then
begin
pProfile.RegisterProfileAs("OfficeReport_Profile");
if (LoadProperties() <> True) then
begin
Init := E_FAIL;
Exit;
end;
end;
pStatusLine := nil;
pConnect.QueryInterface(IID_IStatusLine,pStatusLine);
Init := S_OK;
end;
function TAddInObject.Done: HResult; stdcall;
var n : Integer;
begin
SaveProperties();
if (pErrorLog <> nil) then begin
pErrorLog._Release();
pErrorLog := nil;
end;
if (pEvent <> nil) then begin
pEvent._Release();
pEvent := nil;
end;
if (pProfile <> nil) then begin
pProfile._Release();
pProfile := nil;
end;
if (pStatusLine <> nil) then begin
pStatusLine._Release();
pStatusLine := nil;
end;
if (pConnect <> nil) then begin
n := pConnect._Release();
pConnect := nil;
// ShowMessage("Done = " + IntToStr(n));
if (n > 0) then begin
ShowMessage("Ошибка : Не произошло освобождение интерфейса 1С. Осталось ссылок : "
+ IntToStr(n));
end;
end;
Done := S_OK;
end;
Ну и Application_1C передаю внутрь своих процедур только по ссылке (через var).
Страницы: 1 вся ветка
Текущий архив: 2006.05.07;
Скачать: CL | DM;
Память: 0.56 MB
Время: 0.009 c