我正在尝试使用一个方法创建一个COM类,该方法将一个对象转换为代表VBScript的特定接口。
这是我使用的方法签名:
public object GetInterface(object unknown, string iid)
我认为这是可能的,因为如果方法显式地将返回类型声明为:
public IRequestedInterface GetInterface(object unknown, string iid)
然后,VBScript获得对所需接口的引用。
所以我试着将类型转换为接口
return (IRequestedInterface)unknown;
不幸的是,VBScript得到的是对默认接口的引用,而不是对请求接口的引用。
我已经尝试通过使用ICustomMarshaler创建一个自定义编组程序来解决这个问题。
我认为这是可行的,因为MarshalManagedToNative方法返回一个IntPtr。
因此,我认为如果我只是将IntPtr返回给接口
return Marshal.GetComInterfaceForObject(unknown, typeof(IRequestedInterface));
它会起作用的。但是,很明显,它没有达到预期的效果:
那么,有没有人知道这是不是可能的,你会怎么做?
编辑:
我认为添加一个具体的示例(尽管它是人为的)来解释为什么我不接受VBScript将始终获得默认接口,这将是有帮助的。我仍然坚持我的希望。
下面你会发现3个文件的内容,'TestLib.cs','Build.cmd‘和'Test.vbs’。这些很有希望地证明了为什么我仍然认为这“应该”是可能的。
注意:我已经在Windows XP SP3 (x86)上对此进行了测试。
TestLib.cs
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[assembly: ComVisible(false)]
[assembly: Guid("64e20009-c664-4883-a6e5-1e36a31a0fd8")]
[assembly: AssemblyVersion("2012.06.*")]
[ComVisible(true)]
[Guid("EB77C7B1-D1B9-4BB3-9D63-FBFBD56C9ABA")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IPerformQi
{
[DispId(1000)]
object GetInterface(object unknown, string iid);
[DispId(2000)]
IRequested GetIRequested(object unknown);
}
[ComVisible(true)]
[Guid("7742BC0A-8719-483E-B1DF-AE9CD9A958DC")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDefault
{
[DispId(1000)]
void SayHello(string name);
}
[ComVisible(true)]
[Guid("FFF34296-2A06-47D4-B09C-B93B63D5CC53")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IRequested
{
[DispId(1000)]
void SayGoodbye(string name);
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IPerformQi))]
[Guid("222BB88D-B9FA-4F23-8DB3-BA998F4E668B")]
[ProgId("TestLib.PerformQi")]
public class PerformQi : IPerformQi
{
object IPerformQi.GetInterface(object unknown, string iid)
{
if(iid == "FFF34296-2A06-47D4-B09C-B93B63D5CC53")
return (IRequested)unknown;
throw new Exception("Unable to find inteface");
}
IRequested IPerformQi.GetIRequested(object unknown)
{
return (IRequested)unknown;
}
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IDefault))]
[Guid("174ABED6-3325-4878-89E3-BF8BD1107488")]
[ProgId("TestLib.Test")]
public class Test : IDefault, IRequested
{
void IDefault.SayHello(string name)
{
MessageBox.Show(string.Format("Hello '{0}'", name));
}
void IRequested.SayGoodbye(string name)
{
MessageBox.Show(string.Format("Goodbye '{0}'", name));
}
}Build.cmd
"%windir%\Microsoft.Net\Framework\v4.0.30319\csc.exe" /out:TestLib.dll /target:library /r:System.Windows.Forms.dll TestLib.cs
"%windir%\Microsoft.NET\Framework\v4.0.30319\RegAsm.exe" TestLib.dll /codebase /tlb:TestLib.tlb
PAUSETest.vbs
Dim oPerformQi 'As TestLib.PerformQi
Dim oTest 'As TestLib.Test
Dim oTest2 'As IRequested
Dim oTest3 'As IRequested
Set oPerformQi = CreateObject("TestLib.PerformQi")
Set oTest = CreateObject("TestLib.Test")
Call oTest.SayHello("Robert")
Set oTest2 = oPerformQi.GetIRequested(oTest)
'Note: This works
Call oTest2.SayGoodbye("Robert")
Set oTest3 = oPerformQi.GetInterface(oTest, "FFF34296-2A06-47D4-B09C-B93B63D5CC53")
'Note: This does not work
Call oTest3.SayGoodbye("Robert")使用调用oPerformQi.GetIRequested(oTest)可以使对oTest3.SayGoodbye("Robert")的调用正常工作。这让我觉得你不仅仅局限于VBS的默认界面。
也许由于返回值的隐式强制转换,.Net无法返回指定的接口?理想情况下,我会使用泛型,但我们都知道COM不支持泛型。
在这个限制下,你还能想到其他什么方法来实现这一点吗?
编辑2:
我发现我可以使用VB6来实现这一点,下面是该类的代码。
Option Explicit
Public Function GetInterface(ByVal oUnknown As Object, ByVal IID As String) As Variant
Dim oIRequested As IRequested
If IID = "FFF34296-2A06-47D4-B09C-B93B63D5CC53" Then
Set oIRequested = oUnknown
Set GetInterface = oIRequested
Else
Err.Raise 1, , "Unable to find inteface"
End If
End Function我仍然想找到一个C#版本,如果有人能在这个问题上提供一些启发,我将不胜感激。
发布于 2012-06-29 01:16:30
为了在单个对象上实现多个IDispatch-derived接口,以便可以从脚本环境访问,您应该实现IDispatchEx,并在脚本调用发生时调用其方法。
您面临的问题是这样一个事实,即脚本首先查询您的IDispatch,而您的两个IDispatch-derived接口返回相同的“主”IDispatch,从而没有机会访问其他接口的方法。
当VBS host要调用对象上的方法时,它首先查询IDispatchEx。如果找到,调用将通过IDispatchEx::InvokeEx传递,并且您的COM类可以在内部将调用路由到适当的IDispatch实现,无论是私有实现还是转发到外部/内部对象。
如果找不到IDispatchEx,它会查找IDispatch,这样你就有麻烦了,因为它只看到你的“主”界面。也就是说,您的变通方法是实现IDispatchEx。您可以采用任何一种方法:直接在COM类上实现,或者创建一个代理类来通过IDispatchEx::InvokeEx接受脚本调用并转发到代码中正确的IDispatch。
示例:A和B类都实现了IX和IY接口,B还实现了IDispatchEx。接口方法有IX::X1、IY::Y1。
On Error Resume Next
Set A = CreateObject("Test.A")
WScript.Echo A.X1 ' Success, via IX::Invoke
WScript.Echo A.Y1 ' Failure, A's IDispatch is IX's parent and does not have Y1 method
Set B = CreateObject("Test.B")
WScript.Echo B.X1 ' Success, via IDispatchEx::InvokeEx
WScript.Echo B.Y1 ' Success, via IDispatchEx::InvokeExhttps://stackoverflow.com/questions/11249191
复制相似问题