C# 从.NET Windows服务调用Shell32.dll

C# 从.NET Windows服务调用Shell32.dll,c#,.net,multithreading,com,shell32,C#,.net,Multithreading,Com,Shell32,我有一个.NET 4.0库,它使用Shell32和Folder.getDetailsSof()从WTV文件中获取元数据。我在控制台和Windows窗体应用程序中成功地使用了它,没有问题。但是由于某种原因,当从.NET 4.0 Windows服务调用组件时,调用启动Shell类会导致COM错误 库中出现故障的代码: Shell32.Shell外壳=新外壳() 错误: 无法将“System.\u ComObject”类型的COM对象强制转换为接口类型“Shell32.Shell”。此操作失败,因为对

我有一个.NET 4.0库,它使用Shell32和Folder.getDetailsSof()从WTV文件中获取元数据。我在控制台和Windows窗体应用程序中成功地使用了它,没有问题。但是由于某种原因,当从.NET 4.0 Windows服务调用组件时,调用启动Shell类会导致COM错误

库中出现故障的代码:

Shell32.Shell外壳=新外壳()

错误:

无法将“System.\u ComObject”类型的COM对象强制转换为接口类型“Shell32.Shell”。此操作失败,因为对IID为“{286E6f16lang1024-7113-4355-9562-96B7E9D64C54}”的接口的COM组件的QueryInterface调用失败,原因是以下错误:不支持此类接口(HRESULT的异常:0x80004002(E_NOINTERFACE))


我读了关于公寓线程、COM互操作、Dynamic、PIA等方面的文章,但我发现没有任何解决方案能够解决这个问题。它必须是来自另一个无法看到互操作的线程的调用。请提供帮助:)

我怀疑这可能与以下事实有关:默认情况下,Windows服务没有与桌面交互的权限

为了验证这一理论,请重新配置(至少在临时基础上)您的服务权限以允许桌面交互。下面的链接将引导您完成此操作

更新

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

如果您成功地实现了这一点,请确保您抑制了任何UI交互。有关如何做到这一点的信息,请参见以下答案:


我创建了一个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库(加载然后提取值)

“我的Windows服务”可以通过此功能找到Shell32的不同值,并与类似桌面的用户进行交互;-)


您可以在此找到关于p/Invoke的更多详细信息。我最近在命令行应用程序(控制台)中遇到了同样的问题。事实证明,需要用
[STAThread]
属性注释程序的
Main()
方法。还需要注意的是,如果入口点用
[MTAThread]
注释,它将以完全相同的方式失败。我希望它能有所帮助。

因为我通过搜索错误找到了解决方法,所以我想补充一点,如果您试图从gui应用程序中的非gui线程创建新的Shell(),即使Main用[STAThread]注释,也会发生同样的情况@Eric J的回答给了我足够的暗示,让我从中找到答案


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

关于权限问题,我同意您的看法+1+1,但根据,在Vista和更高版本中,服务根本无法与桌面交互(无论如何,直接交互)。在链接的页面中:“重要服务在Windows Vista中无法直接与用户交互。”。在这种情况下,我不确定我会说“默认”:-)@肯怀特:刚刚选中了Windows8,那个允许交互的复选框仍然存在。我认为这是指无法与登录用户的桌面进行交互。但是,我认为具有该权限的服务仍然可以获得自己的桌面会话。很抱歉,我尝试检查“允许服务与桌面交互”,但收到了相同的错误。有趣的是,如果我只是注释掉Main()中启动服务的代码,然后尝试在那里安装Shell32(然后将应用程序作为控制台应用程序运行),我会得到相同的错误。我甚至尝试了一项没有额外代码的全新服务。Windows服务有什么特别之处,以及它是如何与COM功能进行线程连接和交互的?@RichardScheffrin:一旦你将该应用程序作为服务启动的调用注释掉,该应用程序就没有什么特别之处了。我经常这样做是为了在“控制台模式”下测试我的服务,因为这使调试更加容易。此外,我用新信息更新了我的答案。
[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);