Activator.CreateInstance(<guid>)在VSIDE内部工作,但不在外部</guid>

时间:2011-09-02 22:01:03

标签: c# interop com-interop

我有一堆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做这个工作。无论是否附加调试器,无论是否附加了托管流程,我都会从System.__ComObject返回CreateInstance

当我从控制台窗口编译并运行此代码时,对于某些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 Interop调用成功的任何不同。有什么想法吗?

编辑:

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

编辑2:

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

7 个答案:

答案 0 :(得分:3)

这可能是因为您的应用程序未在Visual Studio外部升级,并且未能获得与COM组件交互的权限。

右键单击并run as administrator以查看它是否有所作为。

答案 1 :(得分:3)

所以我花了一些时间来测试它,我能够完全像你描述的那样重现这个问题。我重新创建了你的确切控制台应用程序,我看到了相同的行为,但我想我至少可以添加一些新信息。

起初我和你一样认为,它是视觉工作室使它工作的东西,但事实并非如此。如果您将其构建为控制台可执行文件,然后从资源管理器中启动它,它可以正常工作,没有视觉工作室参与。另外我在开头添加了Debugger.Launch(),所以当从命令提示符运行时我可以附加它,即使VS完全连接和调试我也会收到错误。我的结果都表明它不是VS正在使它工作,它实际上是从命令提示符运行它正在打破它。

我尝试了各种各样的东西,使命令提示符启动和Windows资源管理器启动之间的环境相同,但我每次都得到同样的东西;从探险家工作完美,从命令行死亡。

使用反射器,设置正在通过所有测试和一切。这是对实际的呼吁:

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

在轰炸的RuntimeType类中,此时没有更多的托管代码可供挖掘。在这一点上,我的猜测是它必须是完全包含在Adobe COM Server中的东西,它在从命令提示符运行时将其终止。

也许知道更多关于Windows内容的人可以说出从命令行执行命令与探险家之间的区别?

答案 2 :(得分:3)

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

在某些情况下,Adobe X(10.1.x)将无法提供IFilter接口。调用QueryInterface,或ClassFactory-&gt; 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);
}

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

答案 3 :(得分:1)

我无法重现你描述的问题...一些检查的一般指示:

答案 4 :(得分:1)

“从技术上讲,adobe提供并正确注册了PDF文本提取过滤器DLL(ACRORDIF.DLL),但它不会通过任何常用方式实例化,即使用LoadIFilter API或在查找后使用直接COM对象创建注册表中的过滤器对象CLSID。它坏了吗?不,因为某种程度上Windows搜索可以使用它!?有些人认为过滤器在STA线程模式下被丢弃(就像在旧的v6天那样)但是没有得到证实通过过滤器DLL的ThreadingModel。有人谈到只通过Job对象运行它.Adobe支持让自己紧张不已,声称限制是为了我们的安全 - 哼哼。 ...“你能猜出这个技巧是如何工作的吗?他们硬编码了MS工具的名称,如PDF过滤器ACRORDIF.DLL中的FILTDUMP !!!所以当PDF IFilter对象被实例化时,它检查调用进程名称,如果它是“白名单”中的一个,它可以工作,否则它会出现问题和E_FAILs。诽谤。为了证明,将程序重命名为“filtdump.exe”,好像通过魔法一切正常,甚至没有作业对象的普通LoadIFilter。“

Does Adobe reader support PDF text extraction or not?

答案 5 :(得分:0)

不确定那里发生了什么,但作为一种解决方法,我想知道你是否可以尝试用类似的东西启动这个过程...

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

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

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

答案 6 :(得分:0)

两个建议。

使用[STAThread]属性。

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

尝试调用CoInitialize

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