Activator.CreateInstance()在VSIDE内部工作,但不在外部工作
我有一堆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 Interop调用成功的任何不同之处。 有任何想法吗?
编辑:
我没有以管理员身份运行VS2010,但我尝试通过提升的Powershell控制台运行输出二进制文件,但它仍然无效。
编辑2:
到目前为止,我用过的唯一一个重现这个“bug”的COM服务器是Acrobat Reader X的AroRdIf.dll(之前的版本工作正常)。 我不担心让Acrobat的特定IFilter工作了,但我非常担心我的代码在我的IDE中运行但不在其中。 而且,顺便说一句,Windows SDK FILTDUMP工具加载这个COM服务器没有问题,所以我知道它是可能的,我只是不知道如何 。
所以我花了一些时间来测试它,我能够完全按照你描述的方式重现这个问题。 我重新创建了你的确切控制台应用程序,我看到了相同的行为,但我想我至少可以添加一些新信息。
起初我和你一样认为,视觉工作室使它成功,但事实并非如此。 如果您将其构建为控制台可执行文件,然后从资源管理器中启动它,它可以正常工作,没有视觉工作室参与。 我还在开头添加了Debugger.Launch(),所以当从命令提示符运行时我可以附加到它,即使VS完全连接和调试我也会收到错误。 我的结果都表明它不是VS正在使它工作,它实际上是从命令提示符运行它打破它。
我尝试了各种各样的东西,使命令提示符启动和Windows资源管理器启动之间的环境相同,但我每次都得到同样的东西; 从资源管理器中完美运行并从命令行中消亡。
使用reflection器挖掘,设置正在通过所有测试和一切。 这是实际的电话:
RuntimeTypeHandle.CreateInstance(this, publicOnly, noCheck, ref canBeCached, ref ctor, ref bNeedSecurityCheck);
在轰炸的RuntimeType类中,此时没有更多的托管代码可供挖掘。 在这一点上,我的猜测是它必须是完全包含在Adobe COM Server中的东西,它在从命令提示符运行时将其终止。
也许有人了解更多关于Windows的内容可以说明从命令行执行命令与探险家之间的区别?
这可能是因为您的应用程序未在Visual Studio外部升级,并且未能获得与COM组件交互的权限。
右键单击并run as administrator
身份run as administrator
,看它是否有所作为。
这个问题很老(并且已经回答),但我想我会添加一些信息。
在某些情况下,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); }
可能存在副作用,即新作业获得当前用户的默认安全性。 我有更多的测试要做。 我欢迎任何人的意见。
我无法重现你描述的问题…一些检查的一般指示:
- 如果您使用默认的Adobe IFilter,那么在64位计算机上运行64位或AnyCPU时会看到奇怪的行为…从VS运行可以在这里产生细微差别
- http://support.microsoft.com/kb/2018558
- http://support.microsoft.com/kb/927675/en-us
- http://social.technet.microsoft.com/Forums/en-US/sharepointsearch/thread/aa966100-17f6-4ea9-8753-085cfbe5f17a/
- http://social.technet.microsoft.com/Forums/en/sharepointsearch/thread/0f062b26-b6ad-4c18-8c33-192e2a741dec
“从技术上讲,adobe提供并正确注册了PDF文本提取filterDLL(ACRORDIF.DLL),但它不会通过任何常见方式实例化,即使用LoadIFilter API或在查找filter对象CLSID后使用直接COM对象创建在注册表中。它被破坏了吗?不,因为某种程度上Windows搜索可以使用它!?有些人认为filter在STA线程模式中被删除(就像在旧的v6天中那样)但是ThreadingModel没有证实这一点。一些人谈到了只通过一个Job对象来运行它.Adobe支持让自己紧张不已,声称限制是为了我们的安全 – 咳咳 。“ …“你能猜出这个技巧是如何工作的吗?他们在PDFfilterACRORDIF.DLL中硬编码了 MS工具的名称 ,比如FILTDUMP !!!所以当实例化PDF IFilter对象时,它会检查调用进程名称,并且如果它是“白名单”中的一个,它就可以工作,否则就会出现问题和E_FAILs。诽谤。为了certificate,将你的程序重命名为“filtdump.exe”,好像通过魔法一切正常,甚至是没有作业对象的普通LoadIFilter。“
Adobe Reader是否支持PDF文本提取?
不知道那里发生了什么,但作为一种解决方法,我想知道你是否可以尝试启动这个过程…
System.Diagnostics.Process.Start("THE_PROCESS.exe");
然后,一旦进程运行,您可以尝试使用ProgID从运行对象表中获取对象…
object appObj = System.Runtime.InteropServices.Marshal.GetActiveObject("THE_PROGID");
两个建议。
使用[STAThread]属性。
[STAThread] static void Main(string[] args) {...}
尝试调用CoInitialize
[DllImport("ole32.dll")] static extern int CoInitialize(IntPtr pvRes); CoInitialize((System.IntPtr)null)