通过Windows服务的COM客户端无法访问C ++ COM服务器

时间:2014-01-30 02:52:59

标签: c# .net visual-studio-2012 windows-7 com

我有一个c#class&除了分配COM对象并执行对进程外C ++ COM服务器的调用之外,我已经提炼出来的方法。

eFilm.IEFilm eFilmInterface = (eFilm.IEFilm)Activator.CreateInstance(Type.GetTypeFromProgID("EFilm.Document", true));
eFilmInterface.oleCloseAllWindows();

当我在命令行上执行此代码作为我的登录用户时,它将生成一个不足的权限错误。如果我然后提升它(或运行升高的外壳),它工作正常。它尝试访问的COM服务器是我登录用户后启动的独立应用程序。

我试图在Windows服务中执行相同的操作。该服务是VS2012 C#windows服务模板的基本实现,我将其标记为.Net 4.0和任何CPU或x86(两者都失败)。我在服务的OnStart实现期间尝试在STA线程中进行上述调用(尽管它在OnStart()方法本身中运行时出现了相同的问题):

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();
}

CreateInstance()调用失败,但出现以下异常:

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)

我尝试将服务帐户从活动会话中的同一用户更改为本地系统。我犯了同样的错误。我有服务验证CLSID存在于其注册表视图中。我没有在服务器的日志中发现任何错误(虽然我没有代码或调试符号)。 Windows应用程序日志显示完全相同的错误(可能是因为它通过链式抛出一直传递出去)。我已经尝试将GetTypeFromProgID切换到远程版本并引用localhost,但它会生成相同的错误。

此外,如果我使用应用程序的命令行版本,并通过NSSM生成服务包装器,我会得到完全相同的错误。

我不确定出了什么问题,也没有一个关于如何继续调试的非常好的计划。关于我可以想到的一切是用户上下文的一些问题,但这似乎是COM应该能做的事情。另一个选择是服务器注册中缺少某些关键信息。 I already had issues with that earlier在这个过程中。

编辑:

经过进一步调查后,似乎对COM服务器的调用导致尝试在运行服务的任何用户上下文中启动该服务器的新副本,而不是活动用户上下文。我试过冒充那个用户,但它没有改变任何东西。如果我使用用户凭据运行服务,您只需在任务管理器中看到为同一用户运行的两个应用程序副本。过程探索者。我怀疑这与服务器的构建或配置方式有关。

有一件事我感兴趣,即使我用LOCAL SYSTEM启动服务模拟COM调用作为活动用户,COM服务器也以SYSTEM而不是用户启动。

EDIT2:

翻阅COM / DCOM设置,在DCOM AppID中看起来像RunAS which lets you choose interactive user。我的服务器从未注册过AppID部分。

我已经手动将AppID部分攻击到AppID和CLSID,但它似乎没有修复它。它仍然在服务的上下文中生成进程,尽管进程的大小没有增长到通常的大小。

1 个答案:

答案 0 :(得分:0)

好的,所以答案似乎是"RunAs" switch for AppID。由于我没有这个服务器的AppID,我必须生成一个。我解决这个问题所需的步骤是:

  1. 为AppID生成新的GUID。启动开发人员提示,然后键入 guidgen.exe 。这将打开一个对话框。选择“注册表格式”选项并将此GUID复制到某处。
  2. 打开文本编辑器并创建一个.reg文件以导入注册表。您将需要以下行。使用来自#1的GUID替换YOUR_NEW_GUID,使用来自服务器的ProgID的CLSID替换SERVER_CLSID。

    Windows注册表编辑器版本5.00

    [HKEY_LOCAL_MACHINE \ SOFTWARE \类\的AppID \ {YOUR_NEW_GUID}] “RunAs”=“互动用户”

    [HKEY_LOCAL_MACHINE \ SOFTWARE \类\ CLSID \ {SERVER_CLSID}] “的AppID”= “{E33A55D3-6743-4F3B-AA90-BB07B9BA3836}”

  3. 如果您的操作系统是64位且您的服务器是32位应用程序,则通过 \ Windows \ syswow64 \ regedit.exe file.reg 更改执行32位注册表file.reg到步骤#2的文件。这将在各种syswow64位置正确安装它们。否则只需使用 regedit.exe file.reg

  4. 理论上,这应该做到。您现在应该能够通过以管理员身份执行 oleview.exe 来确认这是发生的。如果您遇到同样的问题,则需要检查在创建一个AppID之前是否已经注册了该AppID。如果是这样,您可以使用 oleview 并在那里进行编辑。

    在oleview.exe中,在Object Classes>下找到您的组件。所有物体。选择此项,然后在右侧的工作窗口中选择“实施”选项卡,您现在应该看到生成的新AppID GUID。在“激活”选项卡下,您应该看到“启动为交互式用户”。