Re[8]: Com+ , Reference и AppDomain
От: mrozov  
Дата: 16.08.07 07:49
Оценка: 2 (1)
Здравствуйте, mrozov, Вы писали:

M>Здравствуйте, AThe, Вы писали:


M>

M>Ничем не могу помочь.

А хотя... с учетом результата теста №4, можно воспользоваться событием AppDomain.AssemblyResolve
Com+ , Reference и AppDomain
От: AThe  
Дата: 13.08.07 13:00
Оценка:
Есть Com+ компонент, написанный на C#. В его проект добавлены refы на другие библиотеки.
Тесты (создается тестовый проект, создающий объект):
1) Объект не является COM+ компонентом — все работает на ура.
2) Объект является COM+ компонентом — регистрируется в папке Debug тестового проекта. Вызов AppDomain.CreateInstance(MyAsmStr,MyTypeStr) возвращает не то, что в первом тесте. А именно, объект MarshalByRefObject не может быть приведен к типу MyType. При создании всех AppDomain — ApplicationBase == DebugFolder тестового проекта.
3) Зарегистрированный компонент запускается при помощи Службы компонентов — вообще не может найти не один ref, т.к. запускается из %System%.

Вопросы:
1) почему поведение AppDomain.CreateInstaince различно в первом и втором случае?
2) как разрешить зависимости в третьем случае без помещения сборок в ГАК?
Re: Com+ , Reference и AppDomain
От: mrozov  
Дата: 13.08.07 13:40
Оценка:
Здравствуйте, AThe, Вы писали:

AT>Есть Com+ компонент, написанный на C#. В его проект добавлены refы на другие библиотеки.

AT>Тесты (создается тестовый проект, создающий объект):
AT>1) Объект не является COM+ компонентом — все работает на ура.
AT>2) Объект является COM+ компонентом — регистрируется в папке Debug тестового проекта. Вызов AppDomain.CreateInstance(MyAsmStr,MyTypeStr) возвращает не то, что в первом тесте. А именно, объект MarshalByRefObject не может быть приведен к типу MyType. При создании всех AppDomain — ApplicationBase == DebugFolder тестового проекта.
AT>3) Зарегистрированный компонент запускается при помощи Службы компонентов — вообще не может найти не один ref, т.к. запускается из %System%.

AT>Вопросы:

AT>1) почему поведение AppDomain.CreateInstaince различно в первом и втором случае?
AT>2) как разрешить зависимости в третьем случае без помещения сборок в ГАК?

Давненько я этим не занимался, отвечаю по памяти.

1. У тебя в данном случае (по меньшей мере, если речь идет о com-приложении, а не библиотеке) и должен быть MarshalByRefObject. Приводиться он должен к интерфейсу (ты ведь определил интерфейс и назначил ему Guid, правда?).
2. В настройках COM+ приложения есть рабочая папка, которую можно задать.
Re[2]: Com+ , Reference и AppDomain
От: AThe  
Дата: 14.08.07 08:01
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Давненько я этим не занимался, отвечаю по памяти.


M>1. У тебя в данном случае (по меньшей мере, если речь идет о com-приложении, а не библиотеке) и должен быть MarshalByRefObject. Приводиться он должен к интерфейсу (ты ведь определил интерфейс и назначил ему Guid, правда?).

M>2. В настройках COM+ приложения есть рабочая папка, которую можно задать.

Оказалось первая проблема — следствие второй. Вот тестовый пример.

Компонент.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using System.Reflection;
using System.Runtime.Remoting;
using Reference;
using System.IO;

[assembly: ComVisible(true)]
[assembly: Guid("640468B8-2988-4539-B8BA-7C3ED04EB5A8")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationAccessControl(false)]

namespace ComPlusComponent
{
#if(!TEST1)
    [ComVisible(true)]
    [Guid("406FFB62-DC77-47AE-8C78-A31F9DA50F57")]
    [ProgId("ComPlusComponent.1")]
    [ClassInterface(ClassInterfaceType.None)]
#endif
    public class ComPlusComponentClass
#if(!TEST1)
        : ServicedComponent
#endif
    {
        public ComPlusComponentClass()
        {
        }

        static void Log(string message)
        {
            using (StreamWriter sw = new StreamWriter("c:\\ComPlusComponent.log", true))
                sw.WriteLine(message);
        }

        static ComPlusComponentClass()
        {
#if(TEST1)
            Log("Test1");
#endif
#if(TEST2)
            Log("Test2");
#endif
#if(TEST3)
            Log("Test3");
#endif
#if(TEST4)
            Log("Test4");
#endif

            try
            {
#if(TEST1)
                Log(AppDomain.CurrentDomain.BaseDirectory);
                AppDomain domain = AppDomain.CreateDomain("Second Domain");
                ObjectHandle handle = domain.CreateInstanceFrom("Reference.dll", "Reference.ReferenceClass");
                ReferenceClass refer = (ReferenceClass)handle.Unwrap();
                refer.Init();
#endif
#if(TEST2)
                Log(AppDomain.CurrentDomain.BaseDirectory);            
                AppDomain domain = AppDomain.CreateDomain("Second Domain");
                ObjectHandle handle = domain.CreateInstanceFrom("Reference.dll", "Reference.ReferenceClass");
                ReferenceClass refer = (ReferenceClass)handle.Unwrap();
                refer.Init();
#endif
#if(TEST3)
                Log(AppDomain.CurrentDomain.BaseDirectory);            
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = new FileInfo(typeof(ComPlusComponentClass).Assembly.Location).DirectoryName;
                AppDomain domain = AppDomain.CreateDomain("Second Domain", null, setup);
                ObjectHandle handle = domain.CreateInstanceFrom
                    ( "Reference.dll"
                    , "Reference.ReferenceClass");
                object obj = handle.Unwrap();
                Log(obj.GetType().ToString());
                ReferenceClass refer = (ReferenceClass)obj;
                refer.Init();
#endif
#if(TEST4)
                Log(AppDomain.CurrentDomain.BaseDirectory);            
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = new FileInfo(typeof(ComPlusComponentClass).Assembly.Location).DirectoryName;
                Log(setup.ApplicationBase);            
                AppDomain domain = AppDomain.CreateDomain("Second Domain", null, setup);
                ObjectHandle handle = domain.CreateInstanceFrom
                    ( Path.Combine(setup.ApplicationBase, "Reference.dll")
                    , "Reference.ReferenceClass");
                object obj = handle.Unwrap();
                Log(obj.GetType().ToString());
                ReferenceClass refer = (ReferenceClass)obj;
                refer.Init();
#endif
            }
            catch (Exception ex)
            {
                Log(ex.GetType().ToString());
                Log(ex.Message);
            }
        }
    }
}


Сборка от которой зависит комопнент
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Reference
{
    public class ReferenceClass
        : MarshalByRefObject
    {
        public ReferenceClass()
        {
        }

        private void Log(string message)
        {
            using (StreamWriter sw = new StreamWriter("c:\\ComPlusComponent.log", true))
                sw.WriteLine(message);
        }

        public void Init()
        {
            Log("Init Called");
        }
    }
}


Тестовый проект.
using System;
using System.Collections.Generic;
using System.Text;
using ComPlusComponent;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            ComPlusComponentClass c = new ComPlusComponentClass();
        }
    }
}


Вот результаты.

Test1
Z:\Tries\TryComPlus\Test\bin\Debug\
Init Called
Test2
C:\WINDOWS\system32\
System.IO.FileNotFoundException
Could not load file or assembly 'file:///C:\WINDOWS\system32\Reference.dll' or one of its dependencies. Не удается найти указанный файл.
Здесь вставляем путь к Com-dll в Службы компонентов->ComPlusComponent->Активизация->Корневая папка приложения
Test2
C:\WINDOWS\system32\
System.IO.FileNotFoundException
Could not load file or assembly 'file:///C:\WINDOWS\system32\Reference.dll' or one of its dependencies. Не удается найти указанный файл.
Test3
C:\WINDOWS\system32\
System.IO.FileNotFoundException
Could not load file or assembly 'file:///C:\WINDOWS\system32\Reference.dll' or one of its dependencies. Не удается найти указанный файл.
Test4
C:\WINDOWS\system32\
Z:\Tries\TryComPlus\Test\bin\Debug
System.MarshalByRefObject
System.InvalidCastException
Unable to cast transparent proxy to type 'Reference.ReferenceClass'
Переписываем Reference.dll в System32
Test2
C:\WINDOWS\system32\
Init Called
Test3
C:\WINDOWS\system32\
Reference.ReferenceClass
Init Called
Test4
C:\WINDOWS\system32\
Z:\Tries\TryComPlus\Test\bin\Debug
Reference.ReferenceClass
Init Called

Где копать?
Re[3]: Com+ , Reference и AppDomain
От: mrozov  
Дата: 14.08.07 14:40
Оценка:
Здравствуйте, AThe, Вы писали:

AT>Здравствуйте, mrozov, Вы писали:


M>>Давненько я этим не занимался, отвечаю по памяти.


M>>1. У тебя в данном случае (по меньшей мере, если речь идет о com-приложении, а не библиотеке) и должен быть MarshalByRefObject. Приводиться он должен к интерфейсу (ты ведь определил интерфейс и назначил ему Guid, правда?).

M>>2. В настройках COM+ приложения есть рабочая папка, которую можно задать.

AT>Оказалось первая проблема — следствие второй. Вот тестовый пример.

<skiped>
AT>Где копать?

Обязательно выполнить пункт 1.
Без этого к com+ не подходить. Тут штука такая — на первый взгляд может показаться, что оно как бы работает. Но эта видимость, товарищи, только кажущаяся.
Re[4]: Com+ , Reference и AppDomain
От: AThe  
Дата: 15.08.07 08:01
Оценка:
Здравствуйте, mrozov, Вы писали:

M>>>1. У тебя в данном случае (по меньшей мере, если речь идет о com-приложении, а не библиотеке) и должен быть MarshalByRefObject. Приводиться он должен к интерфейсу (ты ведь определил интерфейс и назначил ему Guid, правда?).

M>>>2. В настройках COM+ приложения есть рабочая папка, которую можно задать.

AT>>Оказалось первая проблема — следствие второй. Вот тестовый пример.

M><skiped>
AT>>Где копать?

M>Обязательно выполнить пункт 1.

M>Без этого к com+ не подходить. Тут штука такая — на первый взгляд может показаться, что оно как бы работает. Но эта видимость, товарищи, только кажущаяся.

С этим все в порядке. Просто в тестовом проекте это упущено. Все работало и было оттестировано. Потом в проект добавили новую функциональность — у COM-объекта появились зависимости от библиотек, не помещенных в ГАК и не находящихся в System32. Так что вопрос остался открытым.
Re[5]: Com+ , Reference и AppDomain
От: mrozov  
Дата: 15.08.07 09:07
Оценка:
Здравствуйте, AThe, Вы писали:

AT>С этим все в порядке. Просто в тестовом проекте это упущено. Все работало и было оттестировано. Потом в проект добавили новую функциональность — у COM-объекта появились зависимости от библиотек, не помещенных в ГАК и не находящихся в System32. Так что вопрос остался открытым.


Хм...
Проблема проявляется только при создании объекта в другом домене?

Я правильно понимаю — просто ReferenceClass виден и его можно создать, но вы реализуете систему плагинов.
Код в Test4 показывает, что с получением адреса подключаемой dll вы справляетесь успешно, но полученная сборка с точки зрения runtime отличается от той, с которой запущен проект?

Может, стоит под отладчиком зайти и посмотреть, что там происходит?
Re[6]: Com+ , Reference и AppDomain
От: AThe  
Дата: 15.08.07 10:27
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Хм...

M>Проблема проявляется только при создании объекта в другом домене?

Оказалось, что нет. Был написан еще тест в котором просто выполняется:
      ReferenceClass refer = new ReferenceClass();
      refer.Init();

Говорит : Could not load file or assembly 'Reference, Version=1.0.0.0, Culture=neutral, PublicKeyToken=259e45bab0c62d86' or one of its dependencies. Не удается найти указанный файл.

M>Я правильно понимаю — просто ReferenceClass виден и его можно создать, но вы реализуете систему плагинов.


Получается, что не виден.

M>Код в Test4 показывает, что с получением адреса подключаемой dll вы справляетесь успешно, но полученная сборка с точки зрения runtime отличается от той, с которой запущен проект?

Если я правмльно понимаю, то происходит во что:
1) проект выполняет
ComPlusComponentClass c = new ComPlusComponentClass();

2) из директории System32 запускается dllhost, который создает outproc компонент.
3) в статическом конструкторе компонента создается домен, в котором создается объект класса ReferenceClass и возвращается в домен компонента, который не может найти сборку Reference для приведения типа, поэтому приводит объект к MarshalByRefObject.

M>Может, стоит под отладчиком зайти и посмотреть, что там происходит?

Подтверждается вышесказанное: компонент не видит сборку Reference.dll в рантайме.
Re[7]: Com+ , Reference и AppDomain
От: mrozov  
Дата: 15.08.07 13:09
Оценка:
Здравствуйте, AThe, Вы писали:


Ничем не могу помочь.

У меня подобная схема после прописывания исполняемой директории в качестве корневой для процесса успешно работала. Скорее всего проблема в какой-нибудь мелочи в настройках.
Можно в GAC положить, наверное, в крайнем случае.
Удачи.
Re[8]: Com+ , Reference и AppDomain
От: AThe  
Дата: 16.08.07 04:16
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Можно в GAC положить, наверное, в крайнем случае.

M>Удачи.

Спасибо за ваши ответы.
Re[9]: Com+ , Reference и AppDomain
От: AThe  
Дата: 16.08.07 11:38
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Здравствуйте, mrozov, Вы писали:


M>>Здравствуйте, AThe, Вы писали:


M>>

M>>Ничем не могу помочь.

M>А хотя... с учетом результата теста №4, можно воспользоваться событием AppDomain.AssemblyResolve


Сделал по-другому. Выделил интерфейс IReferenceClass в отдельную сборку и поместил ее в ГАК. Помещение ReferenceDll в ГАК повлекло бы за собой помещение туда же всего проекта, что не очень хорошо. А так, вроде, все сработало. Еще раз спасибо за ваши ответы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.