首先,我必须说我是 C#、.NET 和 COM 互操作的新手。
当我尝试将 COM 对象强制转换为我编写的接口类型时,我收到以下错误消息:
错误消息:无法将“System.__ComObject”类型的 COM 对象转换为接口类型“Observer.IObserver”。此操作失败,因为 IID 为“{13478219-8C3B-4849-99D9-27CEF1A49A55}”的接口的 COM 组件上的 QueryInterface 调用因以下错误而失败:不支持此类接口(来自 HRESULT 的异常:0x80004002 (E_NOINTERFACE)) .
我在 Windows 7 上使用 VS2010 (.NET Framework 3.5)。
这是我的界面(观察者类库项目):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Observer
{
[ComImport]
[Guid("2B2D0BC7-A7C6-4924-A3DE-42F7075E5947")]
public interface IObservable
{
void attach(IObserver observer);
void detach(IObserver observer);
void notify();
}
[ComImport]
[Guid("13478219-8C3B-4849-99D9-27CEF1A49A55")]
public interface IObserver
{
void update(IObservable observable);
}
}
在构建这个 dll 时,我有这个警告:
Observer.dll 不包含任何可以为 COM 互操作注册的类型(肯定是因为接口是在其他 dll 中实现的)。
IObserver 没有出现在我的注册表中(HKEY_CLASSES_ROOT\Interface 中没有)。
这是失败的代码(合成类库项目):
// Arguments de la méthode permettant de récupérer un objet selon son chemin
Object[] args;
// Objet représentant l'état technique de la synthèse
Observer.IObserver pEtatTechniqueSynthese;
args = new Object[] { m_sFullName + "/Etat_Technique" };
pEtatTechniqueSynthese = m_typeSite.InvokeMember("FindObject2",
BindingFlags.InvokeMethod,
null, m_pSite, args) as Observer.IObserver;
//Here pEtatTechniqueSynthese is null
//This call works fine without casting when pEtatTechniqueSynthese's type is Object
//but I need to cast it because my Observer.IObservable's attach method waits for an Observer.IObserver
//If I don't cast I get a "Exception has been thrown by the target of an invocation"
//=> InnerException : "La valeur n'est pas comprise dans la plage attendue"
//I don't know the exact translation, but it sort of means "Value not in expected range"
pEtatTechniqueSynthese = (Observer.IObserver)m_typeSite.InvokeMember("FindObject2",
BindingFlags.InvokeMethod,
null, m_pSite, args);
//Exception raised
pEtatTechniqueSynthese 真实类型 Etat_Technique(Syntheses 类库项目)实现了 Observer.IObserver :
public class Etat_Technique : CODRA.SDK.DotNetUtils.COM.IObjectWithSite, Observer.IObservable, Observer.IObserver, ICalculateurEtatTechnique
我所有的程序集都是 COM 可见的。我的 Observer 项目构建选项“注册 COM 互操作”被选中,我使用强名称密钥文件签署了我的程序集。
我无权访问 COM 服务器代码(第三方组件),但我确信问题来自我的代码。
有人知道我错过了什么吗?
==============================================
有关第三方软件的更多信息:
该软件管理对象。
数据结构似乎是一个对象树,根节点被称为站点。
当一个 dll 类想要访问一个对象时,它必须从站点调用“FindObject2”方法,并将对象路径传递给它。这个方法显然返回一个 COM 对象,所以我们可以调用方法,获取属性,...
我可以开发自己的对象类型并将它们添加到软件中,描述类(指定程序集、类、dll、属性……)。
在那里,我想得到一个我开发的对象并将它从 COM 对象转换回它实现的接口。
将对象声明为 Object 并调用其方法就可以了。
在那里,我需要一个 Observer 将其附加到 Observable 上,所以我必须进行投射。
一个解决方案肯定是将附加方法的参数设为对象,但如果我这样做,使用接口就没有兴趣了。
==============================================
基于Michael Edenfield 的回答的更多信息:
[ComImport 括号打开]
ComImport 属性是一个猜测。我使用它是因为我可以访问一个用于与第三方代码交互的 utils 文件。
这是此文件中定义的接口。当我按照第三方软件文档所说的方式实现它时,我可以访问站点根对象。
我猜如果他们使用它,我也应该使用它。
[ComVisible(true)]
[ComImport]
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352") ]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectWithSite
{
void SetSite([MarshalAs(UnmanagedType.IUnknown)]
[In] object pSite);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
void GetSite([In] ref Guid riid, [Out] out IntPtr pvSite);
}
我会删除这个属性,你比我更了解底层是什么。此外,问题是一样的,所以这个属性是没有用的。
[ComImport 括号关闭]
糟糕的是,FindObject2 有效地返回了一个 {System.__ComObject},所以强制转换不起作用,我被一个 InvalidCastException 卡住了,一次又一次,一次又一次,......
好点是我知道它应该使用什么底层类是。
我在“有关第三方软件的更多信息”部分中谈到的解决方法有效,即使 attach 方法采用 Object 参数,然后管理 Objects 而不是 IObservers。为此,我使用 Reflection 调用 IObserver 的更新方法:
foreach (Object observer in m_observers)
{
Object[] args = new Object[] { this };
observer.GetType().InvokeMember("update",
BindingFlags.InvokeMethod,
null, observer, args);
}
哎呀,不是吗?但是如果我找不到更干净的东西,这个糟糕的东西会完成工作。