Calling Shell32.dll from .NET Windows Service
Asked Answered
C

4

10

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 :)

Chelseychelsie answered 27/1, 2013 at 0:57 Comment(0)
D
14

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.

Derzon answered 26/7, 2014 at 3:38 Comment(0)
K
3

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://mcmap.net/q/589764/-using-shfileoperation-within-a-windows-service

Keening answered 27/1, 2013 at 1:3 Comment(7)
I agree with you about the permission problem. +1Retiform
+1, but according to MSDN, in Vista and higher services can't interact with the desktop at all (directly, anyway). From the page linked: "Important Services cannot directly interact with a user as of Windows Vista.". I'm not sure I'd say "by default" in that case. :-)Pock
@KenWhite: Just checked Windows 8, and that checkbox to allow interaction is still there. I think that refers to not being able to interact with the desktop belonging to the logged-in user. However, I think services with that permission still get their own desktop session.Keening
Sorry, I tried checking allow service to interact with the desktop and received the same error. The interesting part about this is that if I just comment out the code in Main() that starts the service and try to instatiate Shell32 there (then run the app as a console application), I get the same error. I even tried a brand new service with no extra code. What is special about a Windows Service and how it threads and interacts with COM functionality?Chelseychelsie
@RichardScheffrin: There should be nothing special about the app once you comment out the call to start it as a service. I do that frequently to test my services in a "console mode" since that makes debugging much easier. Also, I updated my answer with new information.Keening
Okay, I've been playing with this issue a little. To simplify things a lot, I am closer to understanding the problem. If I create a new ConsoleApplication in Visual Studio, add the reference to Shell32, I see the same result. That attached article says that SHFileOperation is a bad idea in a Windows Service. I guess Shell32 is trying to interact with the system in a way that isn't allowed.Chelseychelsie
@RichardScheffrin: What are you trying to accomplish with Shell32? There may be a different way that is better supported in a Windows Service.Keening
N
0

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

Navarre answered 17/5, 2013 at 9:21 Comment(0)
R
0

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.

Rhodes answered 25/5, 2018 at 0:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.