问题
I have a .NET 4.0 library that uses Shell32 and Folder.GetDetailsOf() to get metadata from WTV files. I have used it successfully with Console and Windows Forms apps without issue. But for some reason, when calling the component from a .NET 4.0 Windows Service, the call to initiate the Shell class causes a COM error.
The code that fails inside the library:
Shell32.Shell shell = new Shell();
The error:
Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{286E6F1B-7113-4355-9562-96B7E9D64C54}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).
I read my fill of Apartment Threading, COM Interops, Dynamic, PIA's, etc, etc, etc :) But no combination of solutions I've found has solved the problem. It must be a calling from another thread that can't see the Interop. Help, please :)
回答1:
I had the same problem just recently with a command line application (console). Turns out, it was required to annotate the program's Main()
method with the [STAThread]
attribute. It has also been noted that it fails miserably in the exact same way if the entry point is annotated with [MTAThread]
instead. I hope it helps.
回答2:
I suspect this may be related to the fact that, by default, a Windows Service does not have permission to interact with the desktop.
To test that theory, reconfigure (at least on a temporary basis) your service permissions to allow for desktop interaction. The following link walks you through doing that
https://superuser.com/questions/415204/how-do-i-allow-interactive-services-in-windows-7
UPDATE
The Shell32 functionality works just fine as LocalSystem, even when the "Allow service to interact with desktop" checkbox is unchecked, but doesn't seem to work at all under a specific user account (whether limited or admin)
Using SHFileOperation within a Windows service
If you succeed in getting this to work, make sure that you suppress any UI interaction. Information on how to do that is available in this answer:
https://stackoverflow.com/a/202519/141172
回答3:
I have created a Windows Service and I called Shell32 with P/Invoke.
In my case, it was to simulating the right click on file :
First, I need to create a process as user (not System) to interact with Desktop :
[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);
And in this process, I used the Shell32 Library (load then extract the value)
[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);
My Windows Service can find the differents values of Shell32 with this and interact with the desktop like user ;-)
You can find more details for P/Invoke on this website
回答4:
Since I found my way here by searching for the error, I wanted to add that the same thing happens if you try to make a new Shell() from a non-gui thread in a GUI app - even when Main is annotated with [STAThread]. @Eric J's answer gave me enough of a hint to figure it out from there.
So if you want Shell() from a GUI app, you need to do the if( mainForm.InvokeRequired ) { mainForm.Invoke( ... ) } dance.
来源:https://stackoverflow.com/questions/14543340/calling-shell32-dll-from-net-windows-service