【问题标题】:Activator.CreateInstance(<guid>) works inside VSIDE but not externallyActivator.CreateInstance(<guid>) 在 VSIDE 内部工作,但不在外部工作
【发布时间】:2011-09-02 22:01:02
【问题描述】:

我有一堆 COM 对象,它们都实现了相同的接口,并且需要在运行时从选项列表中选择其中一个来创建它们。因为我知道每个实现 COM 服务器的 CLSID,所以这应该很容易。但是,对于 COM 库的某个子集,我只能在 VS2010 IDE 内部运行时才能完成这项工作。

这是我用来测试的整个程序:

using System;

namespace ComTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var clsid = "{E8978DA6-047F-4E3D-9C78-CDBE46041603}";
            var type = Type.GetTypeFromCLSID(new Guid(clsid));
            var obj = Activator.CreateInstance(type, true);
            Console.WriteLine("Obj is {0}", obj);
        }
    }
}

只要我通过 VS2010 运行,我可以为迄今为止我尝试过的每个 COM CLSID 进行这项工作。无论是否附加了调试器,无论是否附加了托管进程,我都会从CreateInstance 收到System.__ComObject

当我从控制台窗口编译并运行此代码时,对于某些 CLSID 值,我会得到:

Unhandled Exception: System.Runtime.InteropServices.COMException: Creating an instance of the COM component with CLSID {E8978DA6-047F-4E3D-9C78-CDBE46041603} from the IClassFactory failed due to the following error: 80004005.
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at ComTest.Program.Main(String[] args) in 

这只发生在特定的 CLSID 上——例如,“{c1243ca0-bf96-11cd-b579-08002b30bfeb}”(内置文本 IFilter)有效,但“{E8978DA6-047F-4E3D-9C78-CDBE46041603} "(Acrobat Reader X 的 IFilter)没有。我无法弄清楚通过 IDE 运行对 COM 互操作调用是否成功有何不同。有什么想法吗?

编辑:

我没有以管理员身份运行 VS2010,但我已经尝试通过提升的 Powershell 控制台运行输出二进制文件,但仍然无法正常工作。

编辑 2:

到目前为止,我使用的唯一一个重现此“错误”的 COM 服务器是 Acrobat Reader X 的 AroRdIf.dll(以前的版本可以正常工作)。我不再担心让 Acrobat 的特定 IFilter 工作了,但我非常担心我的代码在我的 IDE 中运行但不在它之外。而且,顺便说一句,Windows SDK FILTDUMP 工具在加载此 COM 服务器时没有问题,所以我知道这是可能的,只是不知道如何

【问题讨论】:

  • 您是在中等信任还是完全信任环境中运行?
  • 或者在 Windows Vista 或 7 上运行并以管理员身份运行 VS?
  • 是的,我知道 Adob​​e“说”他们的 IFilter 在 Windows 7 上“不起作用”,但 Windows SDK 中的 IFilter 测试程序使用 IFilter 从 PDF 中提取文本没有问题,并且我可以在 VS 内做的很好,只是不能在它之外:\
  • 没有复制顺便说一句,当我尝试时工作正常。错误代码很糟糕,E_FAIL 仅表示“无法使其工作,不知道为什么”。对于劣质 COM 服务器来说并非不典型。
  • 人力资源部。也许我会把这个发给我的同事,看看他们中有多少人获得了成功的文本阅读。也许这只是我的环境中的某些东西。谢谢:)

标签: c# interop com-interop


【解决方案1】:

所以我花了一些时间对此进行了测试,并且能够完全按照您的描述重现该问题。我重新创建了与您完全相同的控制台应用程序,并且看到了相同的行为,但我认为我至少可以添加一些新信息。

起初我的想法和你一样,是 Visual Studio 让它工作的东西,但事实并非如此。如果您将它构建到控制台可执行文件中,然后从资源管理器中启动它,它可以正常工作而无需 Visual Studio 参与。此外,我在开头添加了 Debugger.Launch() 以便在从命令提示符运行时附加到它,即使 VS 完全附加和调试,我也会收到错误消息。我的结果都表明它不是 VS 让它工作,它实际上是从命令提示符运行它来破坏它。

我尝试了各种方法来使命令提示符启动和 Windows 资源管理器启动之间的环境相同,但每次都得到相同的结果;在资源管理器中完美运行,从命令行中消失。

深入研究反射器,设置通过了所有测试和一切。这是实际调用:

RuntimeTypeHandle.CreateInstance(this, publicOnly, noCheck, ref canBeCached, ref ctor, ref bNeedSecurityCheck);

在正在爆炸的 RuntimeType 类中,此时没有更多托管代码可供挖掘。在这一点上,我的猜测是它必须完全包含在 Adob​​e COM 服务器中,当从命令提示符运行时它会杀死它。

也许更了解 Windows 的人可以说出从命令行执行与资源管理器执行之间的区别?

【讨论】:

  • 实际上,这可能是个好消息。代码确实需要从服务运行,但如果需要的话,我可以使用“隐藏”的窗口应用程序。我会在最后确认这一点,然后奖励赏金。谢谢!
【解决方案2】:

这可能是因为您的应用程序没有在 Visual Studio 之外提升,并且在与 COM 组件交互的权限上失败。

右键单击并run as administrator 看看是否有区别。

【讨论】:

    【解决方案3】:

    这个问题很旧(并且已回答),但我想我会添加一些信息。

    Adobe X (10.1.x) 在某些情况下将无法提供 IFilter 接口。对 QueryInterface 或 ClassFactory->CreateInstance 或 ::LoadIFilter 的调用或任何将因 E_FAIL 失败的东西。我指的条件是正在运行的进程不是“作业”的一部分。

    即,他们的 10.x IFilter 检查当前进程是否处于任何作业中。如果没有,它会失败(至少对我来说)。我的解决方法类似于以下伪代码:

    HANDLE curProc = GetCurrentProcess();
    BOOL bResult = FALSE;
    int iResult = IsProcessInJob(curProc, NULL, &bResult);
    if(iResult != 0 && bResult == FALSE) {
        HANDLE hJob = CreateJobObject(NULL,"whatever");
        AssignProcessToJob(hJob,curProc);
    }
    

    这可能会产生副作用,即新作业会获得当前用户的默认安全性。我还有更多的测试要做。我欢迎任何人的意见。

    【讨论】:

    • “答案”并没有真正解决问题,而是解释了它发生的原因;我们刚刚放弃使用 Adob​​e 的 IFilter 并购买了一个工具包,这样其他人可能会因此而失眠 :) 不过,这很有趣,我会去看看。
    • 这项技术确实有效,但我随后确定 IPersistFile 不再受支持 - 您必须使用 IPersistStream。
    【解决方案4】:

    我无法重现您描述的问题...一些一般性的检查指针:

    【讨论】:

      【解决方案5】:

      “从技术上讲,adobe 提供并正确注册了 PDF 文本提取过滤器 DLL (ACRORDIF.DLL),但它不会通过任何常用方式实例化,即使用 LoadIFilter API 或在查找后使用直接 COM 对象创建在注册表中过滤对象 CLSID。它坏了吗?不,因为 Windows 搜索可以使用它!?有些人认为过滤器在 STA 线程模式下被删除(就像它在旧的 v6 时代一样),但这没有得到证实“ ...“你能猜出这个技巧是如何工作的吗?他们在 PDF 过滤器 ACRORDIF.DLL 中硬编码了 FILTDUMP 等 MS 工具的名称!!!所以当 PDF IFilter 对象被实例化时,它检查调用进程名称,如果它是“白名单”中的一个,它就可以工作,否则它会伪造问题和 E_FAILs。丑闻。为了证明,将您的程序重命名为“filtdump.exe”,就好像魔法一切正常,甚至没有作业对象的普通 LoadIFilter。”

      Does Adobe reader support PDF text extraction or not?

      【讨论】:

        【解决方案6】:

        不确定那里发生了什么,但作为一种解决方法,我想知道您是否可以尝试使用类似...的方式启动该进程

        System.Diagnostics.Process.Start("THE_PROCESS.exe");
        

        然后,一旦进程运行,您可以尝试使用 ProgID 从运行对象表中获取对象...

        object appObj = System.Runtime.InteropServices.Marshal.GetActiveObject("THE_PROGID");
        

        【讨论】:

        • 这些是进程内 COM 服务器;没有启动过程。我尝试在没有 Activator 对象的情况下手动加载 COM 服务器,但我猜我正在做它正在做的事情,因为我得到了相同的结果。
        • 我也无法重现...只要我编译为 x86 你的示例就可以了。
        • 这样做是一个安全漏洞,除非您先验证 exe 已签名...... System.Diagnostics.Process.Start("THE_PROCESS.exe");
        【解决方案7】:

        两个建议。

        使用 [STAThread] 属性。

        [STAThread]
        static void Main(string[] args)
        {...}
        

        尝试调用 CoInitialize

        [DllImport("ole32.dll")]
        static extern int CoInitialize(IntPtr pvRes);
        CoInitialize((System.IntPtr)null)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-07-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多