从.NET Windows服务调用Shell32.dll

10

我有一个.NET 4.0库,使用Shell32和Folder.GetDetailsOf()从WTV文件中获取元数据。我已经在控制台和Windows Forms应用程序中成功使用过了。但是出现问题的是,在从.NET 4.0 Windows服务调用组件时,初始化Shell类的调用会导致COM错误。

代码库中失败的代码:

Shell32.Shell shell = new Shell();

错误信息:

无法将类型为“System.__ComObject”的COM对象强制转换为接口类型“Shell32.Shell”。这是因为对IID为'{286E6F1B-7113-4355-9562-96B7E9D64C54}'的接口的COM组件进行QueryInterface调用失败,该操作失败的原因是:不支持此类界面(HRESULT:0x80004002 (E_NOINTERFACE))。

我全面阅读了公寓线程、COM Interops、Dynamic、PIA等内容,但我找到的任何解决方案组合都没有解决这个问题。它必须是从另一个线程调用,无法看到Interop。请帮忙,谢谢 :)

4个回答

14

最近我遇到了一个相同的问题,这是一个命令行应用程序(控制台)。事实证明,需要使用 [STAThread] 属性来注释程序的 Main() 方法。同时还注意到,如果入口点被标注为 [MTAThread],它会以完全相同的方式失败。希望能对您有所帮助。


3
我怀疑这可能与默认情况下Windows服务无权与桌面交互有关。
为了测试这个理论,请重新配置(至少在临时基础上)您的服务权限以允许桌面交互。以下链接将指导您完成此操作。

https://superuser.com/questions/415204/how-do-i-allow-interactive-services-in-windows-7

更新

Shell32功能在LocalSystem下运行良好,即使未选中“允许服务与桌面交互”复选框,但在特定用户帐户下(无论是受限还是管理员),似乎根本不起作用。

在Windows服务中使用SHFileOperation

如果您成功使其工作,请确保抑制任何UI交互。有关如何执行此操作的信息可在此答案中找到:

https://stackoverflow.com/a/202519/141172


我同意你对于权限问题的看法。+1 - FrostyFire
+1,但根据MSDN,在Vista及更高版本中,服务无法直接与桌面交互(无论如何)。从链接的页面上可以看到:“重要自Windows Vista起,服务不能直接与用户交互。”在这种情况下,我不确定是否应该说“默认情况下”。 :-) - Ken White
@KenWhite:刚刚检查了Windows 8,允许交互的复选框仍然存在。我认为这是指不能与已登录用户所属的桌面交互。但是,我认为具有该权限的服务仍会获得自己的桌面会话。 - Eric J.
抱歉,我尝试检查允许服务与桌面交互并收到相同的错误。有趣的是,如果我只注释掉Main()中启动服务的代码,并在那里实例化Shell32(然后将应用程序作为控制台应用程序运行),我会得到相同的错误。我甚至尝试了一个全新的没有额外代码的服务。Windows服务和它如何线程和与COM功能交互有什么特别之处? - Richard Scheffrin
@RichardScheffrin:如果您注释掉将其作为服务启动的调用,那么该应用程序就不应该有任何特殊之处。我经常这样做来测试我的服务是否在“控制台模式”下运行,因为这样可以更轻松地进行调试。另外,我已更新我的答案并添加了新信息。 - Eric J.
显示剩余2条评论

0

我已经创建了一个Windows服务,并且使用P/Invoke调用了Shell32。

在我的情况下,这是为了模拟对文件的右键单击:

首先,我需要以用户身份(而不是系统)创建一个进程,与桌面进行交互:

[DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Auto)]
 static extern bool CreateProcessAsUser(
     IntPtr hToken,
     string lpApplicationName,
     string lpCommandLine,
     ref SECURITY_ATTRIBUTES lpProcessAttributes,
     ref SECURITY_ATTRIBUTES lpThreadAttributes,
     bool bInheritHandles,
     uint dwCreationFlags,
     IntPtr lpEnvironment,
     string lpCurrentDirectory,
     ref STARTUPINFO lpStartupInfo,
     out PROCESS_INFORMATION lpProcessInformation);

在这个过程中,我使用了Shell32库(加载然后提取值)。
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(string dllName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);

我的Windows服务可以使用此功能找到Shell32的不同值,并像用户一样与桌面交互 ;-)

您可以在网站上找到有关P/Invoke的更多详细信息


0

由于我是通过搜索错误找到这里的,所以我想补充一下:如果您尝试从非 GUI 线程创建新的 Shell(),即使 Main 带有 [STAThread] 注释,在 GUI 应用程序中也会发生相同的情况。@Eric J 的答案给了我足够的提示,让我从那里解决了问题。

因此,如果您想从 GUI 应用程序中使用 Shell(),则需要执行 if( mainForm.InvokeRequired ) { mainForm.Invoke( ... ) } 操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接