【问题标题】:Call to native DLL fails from a .NET Windows service从 .NET Windows 服务调用本机 DLL 失败
【发布时间】:2012-03-05 17:16:00
【问题描述】:

我有一个本地 dll 形式的第 3 方 API,我使用 DllImport 从 C# 调用它。此原生 dll 依赖于正在打开的 3rd 方应用程序。

当我正常运行代码时,API 会执行预期的操作并驱动应用程序。但是,当我运行与 Windows 服务相同的代码时,即使是我自己,API 也会返回与我在应用程序关闭时看到的相同的(未记录的)错误代码;进程资源管理器确认本机 dll 已从应用程序目录正确加载。

这可能是什么原因造成的,我该如何解决这个问题?

【问题讨论】:

  • 您是否尝试使用虚拟的非托管 dll 来加载以确定错误发生的位置:在 p/invoke 中还是在您的 dll 本身中?
  • @abatishchev - 不,但这绝对不是服务的问题,我已经剥离了服务,因此它所做的只是 p/invoke 非托管 dll。我认为 Fadrian Sudaman 说问题可能与在不同会话中运行的应用程序和服务有关,或者类似的事情是正确的。至少可以说,第 3 方软件相当复杂。
  • 所以来自控制台应用程序的 p/invoke 工作正常,而不是来自 Windows 服务?
  • @abatishchev - 是的,情况就是这样。
  • 当 dll 位于包含空格的路径中时遇到问题

标签: .net windows windows-services pinvoke unmanaged


【解决方案1】:

有点旧,但它是搜索中的热门结果之一。所以我认为我的数据仍然会有所帮助。

我有一个本地 dll 形式的第 3 方 API,我使用 DllImport 从 C# 调用它。此原生 dll 依赖于正在打开的 3rd 方应用程序。

(t)rusty Office Interop DLL 也是如此。您实际上正在启动相关 Office 程序的后台实例。 后台实例需要一个交互式会话,即使它没有显示任何内容(设计假设/工作中的错误)。 服务不在interactive session since Vista 中运行。不推荐使用覆盖。 这是不再使用 Office Interop 的 (3?) 原因之一。

可能的解决方法:

  1. 停止使用服务。 Windows 任务调度程序可以完成相同的工作,同时为您提供完整的交互式会话。甚至 MS 自己也开始尽可能地将东西从服务中移出,移到 Sheduler 中
  2. 将 DLL 访问权限移至 Helper 进程。那个可以在交互式会话中运行。使用任何进程间通信方式在助手和主服务之间进行通信。此模式主要用于处理来自 x64 程序的仅 x32 dll,但它也应该在这里工作。 manifest 和 Process.Start() 都可以从 Service 以交互方式启动程序。

基于其中一个 cmets,您似乎选择了选项 2,但改为使用 Web 服务。 不幸的是,Webserices/Webapplications 通常作为 Windows 服务运行。那是在您考虑它们在一些最严格的权利下运行之前(因为它们可以通过网络访问)。 所以它只会让你回到第 1 格,甚至可以再往后退到第 0 格。

【讨论】:

    【解决方案2】:

    很难说,但我能想到三种可能性:

    • 您的服务具有需要选中“与桌面交互”选项的 UI 组件
    • Windows 服务的工作目录是 %WinDir%\system32(例如 C:\windows\system32),并且您的 dll 中的代码使用相对路径引用其他无法找到的资源
    • 您的服务使用 netpipe 与在两个不同会话中运行的应用程序通信

    【讨论】:

    • 我试过 1. 没有效果。该代码可以在不提供任何路径的情况下从任何位置运行,因此排除了 2。但是,3 听起来很可能——服务在与应用程序不同的会话/域中运行,这导致它无法工作。我该如何验证这一点,如果有解决方法呢?
    • 不幸的是不多。会话隔离是操作系统安全的一部分。您可以在 stackoverflow 或 goggle 上搜索“netpipe 会话隔离”并尝试建议的解决方法,但我个人认为这将是一个相当具有挑战性的旅程。
    • 我认为我要采用的技巧是将 dll 包装在一个 Web 服务中,该服务将在启动时运行,然后从该 Web 服务调用该 Web 服务。这几乎肯定会奏效。无论如何,我希望这个项目很快就能完成:)
    • 使用 Web 服务作为中介来完成这项工作。这是一个讨厌的解决方案,但它是一个讨厌的软件,因此非常适合整体架构:)
    • 看起来你刚刚证明了另一个遗留系统与 WS 的集成:p
    【解决方案3】:

    如果您必须从服务调用本机 dll,在启动它之前,请尝试像这样更改当前目录:

    static void Main()
    {
        System.Environment.CurrentDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
        System.IO.Directory.SetCurrentDirectory(System.AppDomain.CurrentDomain.BaseDirectory);
    
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new XYZService()
        };
        ServiceBase.Run(ServicesToRun);
    }
    

    在此之后,服务无需更改环境变量或无需将本机 DLL 移动到系统文件夹即可轻松找到本机 DLL。

    【讨论】:

      猜你喜欢
      • 2013-01-10
      • 1970-01-01
      • 1970-01-01
      • 2013-05-08
      • 2021-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      相关资源
      最近更新 更多