QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 08.07.08 08:50
Оценка:
Добрый день.
Вопрос в следующем:
Есть класс


    [ComVisible(true)]
    public class CExtender
    {
        private object m_control;
        private string m_name;

        internal CInternalControl(object a_ctrl, string a_name)
        {
            m_control = a_ctrl;
            m_name = a_name;
        }

        string gbfwForms.IControl.Name
        {
            get { return m_name; }
        }
   }

и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.
Re: QueryInterface
От: AB100  
Дата: 08.07.08 11:06
Оценка:
Здравствуйте, grigorash, Вы писали:

G>
G>    [ComVisible(true)]
G>    public class CExtender
G>    {
G>        private object m_control;
G>        private string m_name;

G>        internal CInternalControl(object a_ctrl, string a_name)
G>        {
G>            m_control = a_ctrl;
G>            m_name = a_name;
G>        }

G>        string gbfwForms.IControl.Name
G>        {
G>            get { return m_name; }
G>        }
G>   }
G>

G>и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.

1) как ты достучишся до m_control — это ж private?
2) попробуй через рефлектор получить нужный тебе интерфейс.
Re[2]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 08.07.08 12:19
Оценка:
Здравствуйте, AB100, Вы писали:

AB>1) как ты достучишся до m_control — это ж private?

Вот я и не знаю как вернуть его интерфейс.
AB>2) попробуй через рефлектор получить нужный тебе интерфейс.
Через рефлектор тоже невозможно — объект используют как com объект. Раньше (когда реализация была на с++) нужные интерфейсы получались с помощью QueryInterface, который внутри себя перенаправлял вызов к m_control. Вопрос в том, как мне проделать это (переопределить QueryInterface, или как-то по другому на внешний запрос QI вернуть интерфейс m_control'а) на C#?
Re: QueryInterface
От: Dronkoff Россия  
Дата: 08.07.08 13:46
Оценка:
Здравствуйте, grigorash, Вы писали:
G>и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.

Эммм... как я понял, вызывающему коду (unmanaged), в рантайме необходимо узнать какие интерфейсы реализует экземпляр класса, живущий в m_control, да?

Предлагаю добавить в CExtender публичный метод/свойство GetControlInterfaces, возвращающий Type[] (ну или в каком виде вызывающий код хочет это видеть). В методе рефлекшоном поискать интерфейсы (типа m_control.GetType().FindInterfaces()). Пойдет так?
Re[2]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 09.07.08 07:25
Оценка:
Здравствуйте, Dronkoff, Вы писали:

D>Эммм... как я понял, вызывающему коду (unmanaged), в рантайме необходимо узнать какие интерфейсы реализует экземпляр класса, живущий в m_control, да?


D>Предлагаю добавить в CExtender публичный метод/свойство GetControlInterfaces, возвращающий Type[] (ну или в каком виде вызывающий код хочет это видеть). В методе рефлекшоном поискать интерфейсы (типа m_control.GetType().FindInterfaces()). Пойдет так?


Неа Клиенты есть, их много, менять их никто не будет Нужно добиться именно такого поведения: CExtender->QI(IID_SomeItf) -> возвращается интерфейс реализуемый m_control. m_control — тоже com-объект(activex).
Re[3]: QueryInterface
От: AB100  
Дата: 09.07.08 07:37
Оценка:
Здравствуйте, grigorash, Вы писали:

G>Неа Клиенты есть, их много, менять их никто не будет Нужно добиться именно такого поведения: CExtender->QI(IID_SomeItf) -> возвращается интерфейс реализуемый m_control. m_control — тоже com-объект(activex).


тогда напиши код(IDL) старого сервера(С++) как он реализовывал это CExtender->QI(IID_SomeItf)?
Re[4]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 09.07.08 07:44
Оценка:
Здравствуйте, AB100, Вы писали:

AB>тогда напиши код(IDL) старого сервера(С++) как он реализовывал это CExtender->QI(IID_SomeItf)?


Это не IDL, это уже в самой реализации QI. Грубо говоря, вот так:

HRESULT CExtender::QueryInterface(REFIID iid, void** ppvObject)
{
    // запрашивается интерфейс реализуемый CExtender'ом
    if (IsEqualGUID(iid, IID_IExtender))
    {
        *ppvObject = this;
        return S_OK;
    }
    // все остальные запросы перенаправляем m_control'у
    return m_control->QueryInterface(iid, ppvObject);
}
Re[5]: QueryInterface
От: AB100  
Дата: 09.07.08 09:01
Оценка:
а если так сделать?

 public class CExtender: IMYControl1,IMYControl2
    {
        private object m_control;
        private string m_name;

        internal CInternalControl(object a_ctrl, string a_name)
        {
            m_control = a_ctrl;
            m_name = a_name;
        }

        string gbfwForms.IControl.Name
        {
            get { return m_name; }
        }
       IMYControl1.Method1()
       {
          m_control.Method1(); 
       }
       IMYControl2.Method2()
       {
          m_control.Method2(); 
       }

   }
Re[6]: QueryInterface
От: Аноним  
Дата: 09.07.08 09:13
Оценка:
Здравствуйте, AB100, Вы писали:

AB>а если так сделать?


AB>
AB> public class CExtender: IMYControl1,IMYControl2
AB>


На этапе компиляции интерфейсы поддерживаемые m_control'ом не известны.
Re[7]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 09.07.08 09:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>На этапе компиляции интерфейсы поддерживаемые m_control'ом не известны.

Это не Аноним, это я был
Re[3]: QueryInterface
От: Dronkoff Россия  
Дата: 09.07.08 12:05
Оценка:
Здравствуйте, grigorash, Вы писали:

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


D>>Эммм... как я понял, вызывающему коду (unmanaged), в рантайме необходимо узнать какие интерфейсы реализует экземпляр класса, живущий в m_control, да?


D>>Предлагаю добавить в CExtender публичный метод/свойство GetControlInterfaces, возвращающий Type[] (ну или в каком виде вызывающий код хочет это видеть). В методе рефлекшоном поискать интерфейсы (типа m_control.GetType().FindInterfaces()). Пойдет так?


G>Неа Клиенты есть, их много, менять их никто не будет Нужно добиться именно такого поведения: CExtender->QI(IID_SomeItf) -> возвращается интерфейс реализуемый m_control. m_control — тоже com-объект(activex).


Понятно. Я правдо не шибко шарю в com и interop, но подозреваю что проблему надо решать не в лоб. Например, сделать unmanaged оберточку для managed CExtendor, в которой переопределить QI. В QI вызывать определенный метод CExtendor-а, который будет возвращать нужный интерфейс.

[ComVisible(true)]
public interface IDecapsulate
{
    object GetInterface<T>();
}

[ComVisible(true)]
public class CExtendor : IDecapsulate
{
    private object m_control;

    public CExtendor()
    {
    }

    public T GetInterface<T>()
    {
        if (!T.IsInterface)
            throw new ArgumentException("interfaceType");
        return m_control as T;
    }
}
//Вот он должен быть unmanaged, чтобы была возможность переопределить QI
public class CExtendorWrap
{
    IDecapsulate m_extendor;

    public CExtendorWrap(IDecapsulate extendor)
    {
        m_extendor = extendor;
    }

    public object QueryInterface()
    {
        return m_extendor.GetInterface<IDisposable>();
    }
}
Re[4]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 09.07.08 12:53
Оценка:
Здравствуйте, Dronkoff, Вы писали:

D>Понятно. Я правдо не шибко шарю в com и interop, но подозреваю что проблему надо решать не в лоб. Например, сделать unmanaged оберточку для managed CExtendor, в которой переопределить QI. В QI вызывать определенный метод CExtendor-а, который будет возвращать нужный интерфейс.


Дык, с обёрткой понятно… Хочется без неё, чтоб не ташить лишнего. Хотя, видимо, придётся именно так и поступить. Сейчас читаю про RealProxy и SupportInterface(я вообще-то в .NET новичёк). Если ничего не выйдет, как я понял — обёртка — единственный путь
Re[5]: QueryInterface
От: Dronkoff Россия  
Дата: 09.07.08 14:43
Оценка:
Здравствуйте, grigorash, Вы писали:
D>>Понятно. Я правдо не шибко шарю в com и interop, но подозреваю что проблему надо решать не в лоб. Например, сделать unmanaged оберточку для managed CExtendor, в которой переопределить QI. В QI вызывать определенный метод CExtendor-а, который будет возвращать нужный интерфейс.

G>Дык, с обёрткой понятно… Хочется без неё, чтоб не ташить лишнего. Хотя, видимо, придётся именно так и поступить. Сейчас читаю про RealProxy и SupportInterface(я вообще-то в .NET новичёк). Если ничего не выйдет, как я понял — обёртка — единственный путь


Imho, с оберткой получше будет с архитектурной точки зрения. В первом случае (когда клиенты обращаются к QI) некий прикладной смысл был вложен в платформенные вещи, из-за чего сейчас и возник вопрос сохранения прикладной логики при изменении платформы. И по-моему будет лучше плавно отойти от этого.
Ну, в общем, чем смог, как говорится. Успехов!
Re: QueryInterface
От: aloch Россия  
Дата: 09.07.08 15:19
Оценка: +1
Здравствуйте, grigorash, Вы писали:

Я думаю, на .Net такую задачку не решить никак. Да и в C++ так делать не стоило, т.к. в этом случае нарушается одно из правил COM — от одного объекта всегда должны получаться одинаковые IUnknown. Пусть у меня есть IUnknown для CExtender, я у него порошу какой то интерфейс (например, I2), и QueryInterface возвращает мне интерфейс из m_control. Теперь я прошу IUnknown у I2 и получаю IUnknown не для CExtender, а для m_control.

Для подобных фокусов в COM есть Аггрегация (http://www.codeproject.com/KB/COM/unleashingaggregation.aspx), но, насколько я знаю она не реализуется средствами .Net Interop.


Re[2]: QueryInterface
От: grigorash Россия www.geoserver.ru
Дата: 10.07.08 07:52
Оценка: 12 (2) +1
Здравствуйте, aloch, Вы писали:

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


A>Я думаю, на .Net такую задачку не решить никак. Да и в C++ так делать не стоило, т.к. в этом случае нарушается одно из правил COM — от одного объекта всегда должны получаться одинаковые IUnknown. Пусть у меня есть IUnknown для CExtender, я у него порошу какой то интерфейс (например, I2), и QueryInterface возвращает мне интерфейс из m_control. Теперь я прошу IUnknown у I2 и получаю IUnknown не для CExtender, а для m_control.


Ну, правила-правилами, а даже великий и ужасный VB6 делал именно так (Form.Controls.Item)

A>Для подобных фокусов в COM есть Аггрегация (http://www.codeproject.com/KB/COM/unleashingaggregation.aspx), но, насколько я знаю она не реализуется средствами .Net Interop.

Да вроде как реализуется: CreateAggregatedObject. Только мне не совсем подходит. Решил пойти другим путём (вдруг кому ещё пригодится):
CExtender отнаследовал от MarshalByRefObject. Соорудил следующий класс:


    public class CExtenderProxy : RealProxy, IDisposable
    {
        internal static Guid IID_IdentityUnmarshal
        {
            get {return new Guid("0000001b-0000-0000-c000-000000000046");}
        }
        
        private CExtender m_subject;
        private object    m_control;

        public CExtenderProxy(object subject) : base(subject.GetType())
        {
            AttachServer((MarshalByRefObject)subject);
            m_subject = (CExtender)subject;
            m_control = m_subject.Object; // internal method для CExtender'а
        }

        public void Dispose()
        {
            DetachServer();
        }

        /// нужно определить, т.к. абстрактная в RealProxy
        public override IMessage Invoke(IMessage msg)
        {
            // просто вызываем то, что требуется
            MethodCallMessageWrapper mc = new MethodCallMessageWrapper((IMethodCallMessage)msg);
            MethodInfo mi = (MethodInfo)mc.MethodBase;
            MarshalByRefObject owner = GetUnwrappedServer();
            var outVal = mi.Invoke(owner, mc.Args);
            return new ReturnMessage(outVal, mc.Args, mc.Args.Length, mc.LogicalCallContext, mc);            
        }
        
        // а вот и наш QueryInterface! 
        public override IntPtr SupportsInterface(ref Guid iid)
        {
            //иначе всегда будет виден только m_control(в случае, если он его поддерживает)
            if (iid == IID_IdentityUnmarshal)
                return IntPtr.Zero;

            var result = base.SupportsInterface(ref iid);
            //m_subject интерфейс не поддерживает - спрашиваем у m_control
            if (result == IntPtr.Zero)
            {
                var ptr = Marshal.GetIUnknownForObject(m_control);
                Marshal.QueryInterface(ptr, ref iid, out result);
            }
            return result;
        }
    }


А внешним клиентам отдаю не CExtender напрямую, а следующим образом:


internal object GetExtender(CExtender obj)
{
    return new CExtenderProxy(obj).GetTransparentProxy()
}
.net queryinterface переопределить аггрегация
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.