I have a c# class & method that I've distilled down to doing nothing but allocating a COM object and executing a call to an out-of-process C++ COM Server.
eFilm.IEFilm eFilmInterface = (eFilm.IEFilm)Activator.CreateInstance(Type.GetTypeFromProgID("EFilm.Document", true));
eFilmInterface.oleCloseAllWindows();
When I execute this code on the command line as my login user it will generate an insufficient privileges error. If I then elevate it (or run an elevated shell), it works fine. The COM Server it is attempting to access is a standalone application I start after logging into my user.
I've instead attempted to perform this same action in a Windows Service. The service is a basic implementation of the VS2012 C# windows service template that I've flagged as .Net 4.0 and with Any CPU or x86 (Both fail the same). I attempt the above calls in an STA thread during the service's OnStart implementation (though it exhibited the same issue running in the OnStart() method itself):
protected override void OnStart(string[] args)
{
var staThread = new Thread(this.OnStartSTA);
staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
}
private void OnStartSTA(object threadState)
{
eFilm.IEFilm eFilmInterface = (eFilm.IEFilm)Activator.CreateInstance(Type.GetTypeFromProgID("EFilm.Document", true));
eFilmInterface.oleCloseAllWindows();
}
The CreateInstance() call fails though with the following exception:
System.Runtime.InteropServices.COMException (0x80080005): Retrieving the COM class factory for component with CLSID {C8CF03E4-FD1F-11D3-8C03-0080C8D3C5D3} failed due to the following error: 80080005 Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).
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, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
I've tried changing the Service Account from the same user in the active session all the way up to Local System. I get the same error. I've had the service verify the CLSIDs are present in its view of the registry. I don't find any errors in the log of the server (though I don't have the code or debug symbols for it). The windows application log displays the exact same error (likely because it passes all the way out via chained throws). I've tried switching the GetTypeFromProgID to the remote version and referencing localhost, but it generates the same error.
In addition, if I take the command-line version of the application, and generate a service wrapper via NSSM, I get the exact same error.
I am not sure what is wrong, nor do I have a very good plan on how to continue debugging. About all I can think is it's some issue with user context, but this would seem something COM should be able to do. The other option is that some key information is missing from the Server registration. I already had issues with that earlier in this process.
EDIT:
After further investigation, it seems that the call to the COM Server results in an attempt to start up a new copy of that server in whatever user context the service is running at, not the active user context. I tried impersonating that user, but it didn't change anything. If I run the service with the user credentials, you just see two copies of the application running for the same user in the task manager & process explorer. I suspect this has something to do with how the server was built or configured.
One thing I find interesting, even if I start the service with LOCAL SYSTEM impersonate the COM calls as the active user, the COM server gets started as SYSTEM instead of user.
EDIT2:
Flipping through the COM/DCOM settings, looks like in the DCOM AppID there's a RunAS which lets you choose interactive user. My server never registered an AppID section.
I've manually hacked in the AppID sections to AppID and CLSID, but it doesn't seem to fix it. It still spawns the process in the service's context, though the size of the process doesn't grow to the full size it normally does.