и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.
G>и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.
1) как ты достучишся до m_control — это ж private?
2) попробуй через рефлектор получить нужный тебе интерфейс.
Здравствуйте, AB100, Вы писали:
AB>1) как ты достучишся до m_control — это ж private?
Вот я и не знаю как вернуть его интерфейс. AB>2) попробуй через рефлектор получить нужный тебе интерфейс.
Через рефлектор тоже невозможно — объект используют как com объект. Раньше (когда реализация была на с++) нужные интерфейсы получались с помощью QueryInterface, который внутри себя перенаправлял вызов к m_control. Вопрос в том, как мне проделать это (переопределить QueryInterface, или как-то по другому на внешний запрос QI вернуть интерфейс m_control'а) на C#?
Здравствуйте, grigorash, Вы писали: G>и есть внешние компоненты (не .Net) которые экземпляры этого класса используют. Помимо свойства Name они погут запросить интерфейс, который реализует m_control (на момент компиляции он неизвестен). Раньше я при реализации QI CExtender'а просто перенаправлял ему вызовы. Как сделать это на C#? Спасибо.
Эммм... как я понял, вызывающему коду (unmanaged), в рантайме необходимо узнать какие интерфейсы реализует экземпляр класса, живущий в m_control, да?
Предлагаю добавить в CExtender публичный метод/свойство GetControlInterfaces, возвращающий Type[] (ну или в каком виде вызывающий код хочет это видеть). В методе рефлекшоном поискать интерфейсы (типа m_control.GetType().FindInterfaces()). Пойдет так?
Здравствуйте, Dronkoff, Вы писали:
D>Эммм... как я понял, вызывающему коду (unmanaged), в рантайме необходимо узнать какие интерфейсы реализует экземпляр класса, живущий в m_control, да?
D>Предлагаю добавить в CExtender публичный метод/свойство GetControlInterfaces, возвращающий Type[] (ну или в каком виде вызывающий код хочет это видеть). В методе рефлекшоном поискать интерфейсы (типа m_control.GetType().FindInterfaces()). Пойдет так?
Неа Клиенты есть, их много, менять их никто не будет Нужно добиться именно такого поведения: CExtender->QI(IID_SomeItf) -> возвращается интерфейс реализуемый m_control. m_control — тоже com-объект(activex).
Здравствуйте, grigorash, Вы писали:
G>Неа Клиенты есть, их много, менять их никто не будет Нужно добиться именно такого поведения: CExtender->QI(IID_SomeItf) -> возвращается интерфейс реализуемый m_control. m_control — тоже com-объект(activex).
тогда напиши код(IDL) старого сервера(С++) как он реализовывал это CExtender->QI(IID_SomeItf)?
Здравствуйте, 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, чтобы была возможность переопределить QIpublic class CExtendorWrap
{
IDecapsulate m_extendor;
public CExtendorWrap(IDecapsulate extendor)
{
m_extendor = extendor;
}
public object QueryInterface()
{
return m_extendor.GetInterface<IDisposable>();
}
}
Здравствуйте, Dronkoff, Вы писали:
D>Понятно. Я правдо не шибко шарю в com и interop, но подозреваю что проблему надо решать не в лоб. Например, сделать unmanaged оберточку для managed CExtendor, в которой переопределить QI. В QI вызывать определенный метод CExtendor-а, который будет возвращать нужный интерфейс.
Дык, с обёрткой понятно… Хочется без неё, чтоб не ташить лишнего. Хотя, видимо, придётся именно так и поступить. Сейчас читаю про RealProxy и SupportInterface(я вообще-то в .NET новичёк). Если ничего не выйдет, как я понял — обёртка — единственный путь
Здравствуйте, grigorash, Вы писали: D>>Понятно. Я правдо не шибко шарю в com и interop, но подозреваю что проблему надо решать не в лоб. Например, сделать unmanaged оберточку для managed CExtendor, в которой переопределить QI. В QI вызывать определенный метод CExtendor-а, который будет возвращать нужный интерфейс.
G>Дык, с обёрткой понятно… Хочется без неё, чтоб не ташить лишнего. Хотя, видимо, придётся именно так и поступить. Сейчас читаю про RealProxy и SupportInterface(я вообще-то в .NET новичёк). Если ничего не выйдет, как я понял — обёртка — единственный путь
Imho, с оберткой получше будет с архитектурной точки зрения. В первом случае (когда клиенты обращаются к QI) некий прикладной смысл был вложен в платформенные вещи, из-за чего сейчас и возник вопрос сохранения прикладной логики при изменении платформы. И по-моему будет лучше плавно отойти от этого.
Ну, в общем, чем смог, как говорится. Успехов!
Я думаю, на .Net такую задачку не решить никак. Да и в C++ так делать не стоило, т.к. в этом случае нарушается одно из правил COM — от одного объекта всегда должны получаться одинаковые IUnknown. Пусть у меня есть IUnknown для CExtender, я у него порошу какой то интерфейс (например, I2), и QueryInterface возвращает мне интерфейс из m_control. Теперь я прошу IUnknown у I2 и получаю IUnknown не для CExtender, а для m_control.
Здравствуйте, 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();
}
/// нужно определить, т.к. абстрактная в RealProxypublic 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_controlif (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()
}