COM 返回未实现任何接口的类型

发布于 2024-12-10 04:11:52 字数 2587 浏览 0 评论 0原文

我需要通过 .NET 4.0 应用程序自动执行 Adob​​e InDesign CS3 中的某些任务。我已使用 Visual Studio 中的“添加引用”对话框添加了对 InDesign 类型库的引用。它生成一个互操作程序集,该程序集正确包含类型库中声明的所有接口和类型。我没有安装任何 Adob​​e SDK,因为类型库可以在 Visual Studio 中使用,而无需安装除 Adob​​e InDesign CS3 之外的任何内容。

现在对我而言,互操作程序集中有趣的类型是接口 _ApplicationApplication 以及类 ApplicationClass。下面是它们的定义,因此您可以看到它们之间的关系:

public interface _Application
{
    // Lots of properties and methods
}

public interface Application : _Application
{
    // Empty
}

public class ApplicationClass : _Application, Application
{
    // The same properties and methods as declared in _Application
}

我尝试像这样实例化 COM 对象:

Type oType = Type.GetTypeFromProgID("InDesign.Application.CS3");
if (oType != null)
{
    object instance = Activator.CreateInstance(oType);
}

此代码成功。我得到一个实例,但其类型为 __ComObject。据我所知,这是完全正常的。

现在,乐趣开始了。为了使这个实例对我可用,我应该将其转换为正确的接口。来自网络上的其他示例,以及来自此处的 文档,我可以看到我应该将其转换为 Application 接口。但如果我这样做,我会收到一个令人讨厌的 InvalidCastException 消息,指出类型 System.__ComObject 不支持此接口。如果我尝试将其转换为 ApplicationClass_Application 接口,我会得到相同的异常。

我认为我可能使用了不正确的界面,所以我尝试实现实用程序函数 此处列出。该函数循环访问互操作程序集中声明的所有接口,并查询 IUnknown 接口(如果它实现了该接口)。

当我使用该函数时,它返回 null,这意味着我从 CreateInstance 返回的实例不支持互操作程序集中的任何接口。当然,这不可能是对的!?

但是,如果我使用 Visual Studio 调试器检查 instance 变量,就会发现有一个称为“动态视图”的东西。如果我展开它,它会列出该对象的所有属性,并且所有属性都与 ApplicationClass 类和 _Application 接口中的属性相匹配。所以我尝试使用 Type.InvokeMember ,它确实有效:

oType.InvokeMember("DoScript", BindingFlags.InvokeMethod, null, instance, oArguments, CultureInfo.InvariantCulture);

这确实有效,但是像这样与 COM 对象交互会非常麻烦,而且我需要与 COM 对象进行大量交互,所以这并不是真正有用的。我想我可以为 COM 对象创建一个包装器,但这违背了互操作程序集的目的,而且我不想创建 700 多个包装器类。

我进行了大量搜索,找到了使用 InDesign COM 对象的教程和示例,但它们都只是将实例转换为返回到应用程序接口,但正如所解释的,这在我的情况下不起作用。

我还尝试了以下代码而不是上面的代码:

InDesign.Application app = new InDesign.Application();
app.Activate();

第一行成功,我得到了 ApplicationClass 的实例,但是当它尝试执行第二行时,我得到一个 InvalidCastException ,指出 ApplicationClass 无法转换为接口_Application

我真的被困在这里,不知道下一步该尝试什么。我真的希望对 COM 和 .NET 更有经验的人能够了解我可能做错了什么。

预先感谢,并对这么长的帖子表示歉意。

I need to automate some tasks in Adobe InDesign CS3 from a .NET 4.0 application. I've added a reference to the InDesign Type Library using the Add Reference dialog in Visual Studio. It genereates an interop assembly, which correctly includes all of the interfaces and types declared in the type library. I haven't installed any Adobe SDK, as the type library was available in Visual Studio without installing anything but Adobe InDesign CS3.

The interesting types in the interop assembly for me right now is the interfaces _Application and Application, and the class ApplicationClass. Here is the definition of them, so you can see the relationship between them:

public interface _Application
{
    // Lots of properties and methods
}

public interface Application : _Application
{
    // Empty
}

public class ApplicationClass : _Application, Application
{
    // The same properties and methods as declared in _Application
}

I try to instantiate the COM object like this:

Type oType = Type.GetTypeFromProgID("InDesign.Application.CS3");
if (oType != null)
{
    object instance = Activator.CreateInstance(oType);
}

This code succeeds. I get an instance, but of a type called __ComObject. From what I know, this is completely normal.

Now, here's where the fun begins. For this instance to be usable to me, I should cast it to the correct interface. From other examples on the net, and from the documentation available here, I can see I should cast it to the Application interface. But if I do that, I get a nasty InvalidCastException saying that the type System.__ComObject doesn't support this interface. I get the same exception if I try to cast it to ApplicationClass or the _Application interface.

I thought I was perhaps using an incorrect interface, so I tried implementing the utility function listed here. This function loops through all the interfaces declared in the interop assembly and queries the IUnknown interface if it implements the interface.

When I use that function, it returns null, meaning that the instance I get back from CreateInstance supports none of the interfaces in the interop assembly. Surely, that can't be right!?

However, if I inspect the instance variable with the Visual Studio Debugger, there's something called "Dynamic View". If I expand that, it lists all the properties for the object and all the properties match the properties from the ApplicationClass class and the _Application interface. So I tried using Type.InvokeMember and that works:

oType.InvokeMember("DoScript", BindingFlags.InvokeMethod, null, instance, oArguments, CultureInfo.InvariantCulture);

This actually works, but it would be extremely cumbersome to interact with the COM object like this, and I need to do alot of interaction with the COM object, so this is not really usable. I guess I could make a wrapper for the COM objects, but that kind of defeats the purpose of the interop assembly, and I don't want to create 700+ wrapper classes.

I've searched alot, and I've found tutorials and examples of using the InDesign COM object, but they all just cast the instance returned to the Application interface, but as explained, this doesn't work in my case.

I've also tried the following code instead of the code above:

InDesign.Application app = new InDesign.Application();
app.Activate();

The first line succeeds and I get an instance of ApplicationClass, but when it tries to execute the second line I get a InvalidCastException stating that ApplicationClass cannot be converted to the interface _Application.

I'm really cornered up here, and not sure what to try next. I really hope someone more experienced with COM and .NET has an idea of what I could be doing wrong.

Thanks in advance, and sorry for such a long post.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

能否归途做我良人 2024-12-17 04:11:52

您必须使用运行时可调用包装器

您需要的方法是此一个

试试这个:

    Type oType = Type.GetTypeFromProgID("InDesign.Application.CS3");
    if (oType != null)
    {
        object instance = Activator.CreateInstance(oType);// or any other way you can get it
        Application app = 
            (Application)System.Runtime.InteropServices.Marshal.CreateWrapperOfType(instance, typeof(ApplicationClass));
    }

You have to use a Runtime Callable Wrapper

The method you need is this one

Try this :

    Type oType = Type.GetTypeFromProgID("InDesign.Application.CS3");
    if (oType != null)
    {
        object instance = Activator.CreateInstance(oType);// or any other way you can get it
        Application app = 
            (Application)System.Runtime.InteropServices.Marshal.CreateWrapperOfType(instance, typeof(ApplicationClass));
    }
只是在用心讲痛 2024-12-17 04:11:52

@Seb 解决方案是正确的,但在处理它时,我遇到了错误“源对象无法转换为目标类型,因为它不支持所有必需的接口。” (正如@MikeKeskinov 在评论中所写)。

要消除该错误,您需要通过执行以下步骤来注册 COM 对象(已在此处进行了解释) - 这是为了InDesign CC2021 v16.0(其他版本类似)

  • 使用任务管理器关闭 InDesign 的所有实例
  • 转到 C:\ProgramData\Adobe\InDesign\Version 16.0\en_US\Scripting Support\16.0 并将 tlb 文件重命名为 tlb.bak(或类似的名称,没关系)
  • 以管理员身份打开命令窗口 (CMD)
  • 转到 InDesign EXE 文件夹 C:\ Program Files\Adobe\Adobe InDesign 2021
  • 启动命令 indesign.exe -type
  • 将新创建的 tlb 文件引用到项目

然后它应该可以工作。希望我对某人有所帮助!

@Seb solution is the correct one, but while working on it, I came across error "Source object cannot be converted to the destination type since it does not support all the required interfaces." (as @MikeKeskinov wrote it in comments).

To get rid of that error, you need to register COM objects by doing these steps (already explained here) - this is for InDesign CC2021 v16.0 (it's similar for other versions)

  • Close all instances of InDesign using Task Manager
  • Go to C:\ProgramData\Adobe\InDesign\Version 16.0\en_US\Scripting Support\16.0 and rename tlb file to tlb.bak (or something similar, it doesn't matter)
  • Open command window (CMD) as administrator
  • Go to InDesign EXE folder C:\Program Files\Adobe\Adobe InDesign 2021
  • Start command indesign.exe -type
  • Reference newly created tlb file to project

And after that it should work. Hope I helped to someone!

枫林﹌晚霞¤ 2024-12-17 04:11:52

我最近没有使用 COM,但据我记得(并理解你的问题)你不能像这样转换 ComObject:

   Application app = (Application)comObject;

但你应该使用 as 运算符:

    Application = comObject as Application;

I didn't work with COM recently but as far I remember (and understood your problem) you can not cast ComObject like this:

   Application app = (Application)comObject;

but you should use as operator:

    Application = comObject as Application;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文